diff --git a/.ci/jobs.yml b/.ci/jobs.yml index d4ec8a3d5a699..f62ec9510d2d4 100644 --- a/.ci/jobs.yml +++ b/.ci/jobs.yml @@ -1,4 +1,4 @@ -# This file is needed by functionalTests:ensureAllTestsInCiGroup for the list of ciGroups. That must be changed before this file can be removed +# This file is needed by node scripts/ensure_all_tests_in_ci_group for the list of ciGroups. That must be changed before this file can be removed JOB: - kibana-intake diff --git a/.node-version b/.node-version index 19c4c189d3640..c91434ab584a7 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -14.15.3 +14.15.4 diff --git a/.nvmrc b/.nvmrc index 19c4c189d3640..c91434ab584a7 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -14.15.3 +14.15.4 diff --git a/NOTICE.txt b/NOTICE.txt index bf3cb4aa4ac87..2341a478cbda9 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -1,5 +1,5 @@ Kibana source code with Kibana X-Pack source code -Copyright 2012-2020 Elasticsearch B.V. +Copyright 2012-2021 Elasticsearch B.V. --- Pretty handling of logarithmic axes. diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md index d73ed716e6b19..b0730d1b762d6 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md @@ -44,6 +44,7 @@ readonly links: { readonly aggs: { readonly date_histogram: string; readonly date_range: string; + readonly date_format_pattern: string; readonly filter: string; readonly filters: string; readonly geohash_grid: string; @@ -104,5 +105,11 @@ readonly links: { readonly ml: Record; readonly transforms: Record; readonly visualize: Record; + readonly apis: Record; + readonly observability: Record; + readonly alerting: Record; + readonly maps: Record; + readonly monitoring: Record; + readonly security: Record; }; ``` diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md index 7aa170eef9b50..2d06876f42b6a 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md @@ -17,5 +17,5 @@ export interface DocLinksStart | --- | --- | --- | | [DOC\_LINK\_VERSION](./kibana-plugin-core-public.doclinksstart.doc_link_version.md) | string | | | [ELASTIC\_WEBSITE\_URL](./kibana-plugin-core-public.doclinksstart.elastic_website_url.md) | string | | -| [links](./kibana-plugin-core-public.doclinksstart.links.md) | {
readonly dashboard: {
readonly guide: string;
readonly drilldowns: string;
readonly drilldownsTriggerPicker: string;
readonly urlDrilldownTemplateSyntax: string;
readonly urlDrilldownVariables: string;
};
readonly filebeat: {
readonly base: string;
readonly installation: string;
readonly configuration: string;
readonly elasticsearchOutput: string;
readonly startup: string;
readonly exportedFields: string;
};
readonly auditbeat: {
readonly base: string;
};
readonly metricbeat: {
readonly base: string;
};
readonly heartbeat: {
readonly base: string;
};
readonly logstash: {
readonly base: string;
};
readonly functionbeat: {
readonly base: string;
};
readonly winlogbeat: {
readonly base: string;
};
readonly aggs: {
readonly date_histogram: string;
readonly date_range: string;
readonly filter: string;
readonly filters: string;
readonly geohash_grid: string;
readonly histogram: string;
readonly ip_range: string;
readonly range: string;
readonly significant_terms: string;
readonly terms: string;
readonly avg: string;
readonly avg_bucket: string;
readonly max_bucket: string;
readonly min_bucket: string;
readonly sum_bucket: string;
readonly cardinality: string;
readonly count: string;
readonly cumulative_sum: string;
readonly derivative: string;
readonly geo_bounds: string;
readonly geo_centroid: string;
readonly max: string;
readonly median: string;
readonly min: string;
readonly moving_avg: string;
readonly percentile_ranks: string;
readonly serial_diff: string;
readonly std_dev: string;
readonly sum: string;
readonly top_hits: string;
};
readonly scriptedFields: {
readonly scriptFields: string;
readonly scriptAggs: string;
readonly painless: string;
readonly painlessApi: string;
readonly painlessSyntax: string;
readonly luceneExpressions: string;
};
readonly indexPatterns: {
readonly loadingData: string;
readonly introduction: string;
};
readonly addData: string;
readonly kibana: string;
readonly siem: {
readonly guide: string;
readonly gettingStarted: string;
};
readonly query: {
readonly eql: string;
readonly luceneQuerySyntax: string;
readonly queryDsl: string;
readonly kueryQuerySyntax: string;
};
readonly date: {
readonly dateMath: string;
};
readonly management: Record<string, string>;
readonly ml: Record<string, string>;
readonly transforms: Record<string, string>;
readonly visualize: Record<string, string>;
} | | +| [links](./kibana-plugin-core-public.doclinksstart.links.md) | {
readonly dashboard: {
readonly guide: string;
readonly drilldowns: string;
readonly drilldownsTriggerPicker: string;
readonly urlDrilldownTemplateSyntax: string;
readonly urlDrilldownVariables: string;
};
readonly filebeat: {
readonly base: string;
readonly installation: string;
readonly configuration: string;
readonly elasticsearchOutput: string;
readonly startup: string;
readonly exportedFields: string;
};
readonly auditbeat: {
readonly base: string;
};
readonly metricbeat: {
readonly base: string;
};
readonly heartbeat: {
readonly base: string;
};
readonly logstash: {
readonly base: string;
};
readonly functionbeat: {
readonly base: string;
};
readonly winlogbeat: {
readonly base: string;
};
readonly aggs: {
readonly date_histogram: string;
readonly date_range: string;
readonly date_format_pattern: string;
readonly filter: string;
readonly filters: string;
readonly geohash_grid: string;
readonly histogram: string;
readonly ip_range: string;
readonly range: string;
readonly significant_terms: string;
readonly terms: string;
readonly avg: string;
readonly avg_bucket: string;
readonly max_bucket: string;
readonly min_bucket: string;
readonly sum_bucket: string;
readonly cardinality: string;
readonly count: string;
readonly cumulative_sum: string;
readonly derivative: string;
readonly geo_bounds: string;
readonly geo_centroid: string;
readonly max: string;
readonly median: string;
readonly min: string;
readonly moving_avg: string;
readonly percentile_ranks: string;
readonly serial_diff: string;
readonly std_dev: string;
readonly sum: string;
readonly top_hits: string;
};
readonly scriptedFields: {
readonly scriptFields: string;
readonly scriptAggs: string;
readonly painless: string;
readonly painlessApi: string;
readonly painlessSyntax: string;
readonly luceneExpressions: string;
};
readonly indexPatterns: {
readonly loadingData: string;
readonly introduction: string;
};
readonly addData: string;
readonly kibana: string;
readonly siem: {
readonly guide: string;
readonly gettingStarted: string;
};
readonly query: {
readonly eql: string;
readonly luceneQuerySyntax: string;
readonly queryDsl: string;
readonly kueryQuerySyntax: string;
};
readonly date: {
readonly dateMath: string;
};
readonly management: Record<string, string>;
readonly ml: Record<string, string>;
readonly transforms: Record<string, string>;
readonly visualize: Record<string, string>;
readonly apis: Record<string, string>;
readonly observability: Record<string, string>;
readonly alerting: Record<string, string>;
readonly maps: Record<string, string>;
readonly monitoring: Record<string, string>;
readonly security: Record<string, string>;
} | | diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableinput.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableinput.md index 0f14215ff1309..07ede291e33d2 100644 --- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableinput.md +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableinput.md @@ -17,5 +17,6 @@ export declare type EmbeddableInput = { disabledActions?: string[]; disableTriggers?: boolean; searchSessionId?: string; + syncColors?: boolean; }; ``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.openaddpanelflyout.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.openaddpanelflyout.md index ce97f79b4beb9..add4646375359 100644 --- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.openaddpanelflyout.md +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.openaddpanelflyout.md @@ -14,7 +14,7 @@ export declare function openAddPanelFlyout(options: { overlays: OverlayStart; notifications: NotificationsStart; SavedObjectFinder: React.ComponentType; -}): Promise; +}): OverlayRef; ``` ## Parameters @@ -25,5 +25,5 @@ export declare function openAddPanelFlyout(options: { Returns: -`Promise` +`OverlayRef` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler._constructor_.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler._constructor_.md index 1565202e84674..9dfad91c33679 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler._constructor_.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler._constructor_.md @@ -9,7 +9,7 @@ Constructs a new instance of the `ExpressionRenderHandler` class Signature: ```typescript -constructor(element: HTMLElement, { onRenderError, renderMode, hasCompatibleActions, }?: ExpressionRenderHandlerParams); +constructor(element: HTMLElement, { onRenderError, renderMode, syncColors, hasCompatibleActions, }?: ExpressionRenderHandlerParams); ``` ## Parameters @@ -17,5 +17,5 @@ constructor(element: HTMLElement, { onRenderError, renderMode, hasCompatibleActi | Parameter | Type | Description | | --- | --- | --- | | element | HTMLElement | | -| { onRenderError, renderMode, hasCompatibleActions, } | ExpressionRenderHandlerParams | | +| { onRenderError, renderMode, syncColors, hasCompatibleActions, } | ExpressionRenderHandlerParams | | diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.md index d65c06bdaed83..1a7050f3ffd4e 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.md @@ -14,7 +14,7 @@ export declare class ExpressionRenderHandler | Constructor | Modifiers | Description | | --- | --- | --- | -| [(constructor)(element, { onRenderError, renderMode, hasCompatibleActions, })](./kibana-plugin-plugins-expressions-public.expressionrenderhandler._constructor_.md) | | Constructs a new instance of the ExpressionRenderHandler class | +| [(constructor)(element, { onRenderError, renderMode, syncColors, hasCompatibleActions, })](./kibana-plugin-plugins-expressions-public.expressionrenderhandler._constructor_.md) | | Constructs a new instance of the ExpressionRenderHandler class | ## Properties diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md index 22a73fff039e6..4ef1225ae0d7e 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md @@ -25,6 +25,7 @@ export interface IExpressionLoaderParams | [renderMode](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.rendermode.md) | RenderMode | | | [searchContext](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.searchcontext.md) | SerializableState | | | [searchSessionId](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.searchsessionid.md) | string | | +| [syncColors](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.synccolors.md) | boolean | | | [uiState](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.uistate.md) | unknown | | | [variables](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.variables.md) | Record<string, any> | | diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.synccolors.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.synccolors.md new file mode 100644 index 0000000000000..619f54ad88ef2 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.synccolors.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [IExpressionLoaderParams](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md) > [syncColors](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.synccolors.md) + +## IExpressionLoaderParams.syncColors property + +Signature: + +```typescript +syncColors?: boolean; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.issynccolorsenabled.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.issynccolorsenabled.md new file mode 100644 index 0000000000000..6cdc796bf464b --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.issynccolorsenabled.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [IInterpreterRenderHandlers](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md) > [isSyncColorsEnabled](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.issynccolorsenabled.md) + +## IInterpreterRenderHandlers.isSyncColorsEnabled property + +Signature: + +```typescript +isSyncColorsEnabled: () => boolean; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md index c22c8bc6b6245..0b39a9b4b3ea2 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md @@ -18,6 +18,7 @@ export interface IInterpreterRenderHandlers | [event](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.event.md) | (event: any) => void | | | [getRenderMode](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.getrendermode.md) | () => RenderMode | | | [hasCompatibleActions](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.hascompatibleactions.md) | (event: any) => Promise<boolean> | | +| [isSyncColorsEnabled](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.issynccolorsenabled.md) | () => boolean | | | [onDestroy](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.ondestroy.md) | (fn: () => void) => void | | | [reload](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.reload.md) | () => void | | | [uiState](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.uistate.md) | unknown | This uiState interface is actually PersistedState from the visualizations plugin, but expressions cannot know about vis or it creates a mess of circular dependencies. Downstream consumers of the uiState handler will need to cast for now. | diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.issynccolorsenabled.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.issynccolorsenabled.md new file mode 100644 index 0000000000000..71a7e020e65a5 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.issynccolorsenabled.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [IInterpreterRenderHandlers](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md) > [isSyncColorsEnabled](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.issynccolorsenabled.md) + +## IInterpreterRenderHandlers.isSyncColorsEnabled property + +Signature: + +```typescript +isSyncColorsEnabled: () => boolean; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md index 547608f40e6aa..831c9023c7e48 100644 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md @@ -18,6 +18,7 @@ export interface IInterpreterRenderHandlers | [event](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.event.md) | (event: any) => void | | | [getRenderMode](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.getrendermode.md) | () => RenderMode | | | [hasCompatibleActions](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.hascompatibleactions.md) | (event: any) => Promise<boolean> | | +| [isSyncColorsEnabled](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.issynccolorsenabled.md) | () => boolean | | | [onDestroy](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.ondestroy.md) | (fn: () => void) => void | | | [reload](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.reload.md) | () => void | | | [uiState](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.uistate.md) | unknown | This uiState interface is actually PersistedState from the visualizations plugin, but expressions cannot know about vis or it creates a mess of circular dependencies. Downstream consumers of the uiState handler will need to cast for now. | diff --git a/docs/discover/search.asciidoc b/docs/discover/search.asciidoc index 75c6fddb484ac..45f0df5bd773f 100644 --- a/docs/discover/search.asciidoc +++ b/docs/discover/search.asciidoc @@ -74,7 +74,7 @@ status codes, you could enter `status:[400 TO 499]`. codes and have an extension of `php` or `html`, you could enter `status:[400 TO 499] AND (extension:php OR extension:html)`. -IMPORTANT: When you use the Lucene Query Syntax in the *KQL* search bar, {kib} is unable to search on nested objects and perform aggregations across fields that contain nested objects. +IMPORTANT: When you use the Lucene Query Syntax in the *KQL* search bar, {kib} is unable to search on nested objects and perform aggregations across fields that contain nested objects. Using `include_in_parent` or `copy_to` as a workaround can cause {kib} to fail. For more detailed information about the Lucene query syntax, see the @@ -107,7 +107,8 @@ To save the current search: . Click *Save* in the Kibana toolbar. . Enter a name for the search and click *Save*. -To import, export, and delete saved searches, open the main menu, then click *Stack Management > Saved Ojbects*. +To import, export, and delete saved searches, open the main menu, +then click *Stack Management > Saved Objects*. ==== Open a saved search To load a saved search into Discover: diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index 99fadb240335a..7e7c8953fd527 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -262,6 +262,10 @@ Hides the "Time" column in *Discover* and in all saved searches on dashboards. Highlights results in *Discover* and saved searches on dashboards. Highlighting slows requests when working on big documents. +[[doctable-legacy]]`doc_table:legacy`:: +Control the way the Discover's table looks and works. Set this property to `true` to revert to the legacy implementation. + + [float] [[kibana-ml-settings]] ==== Machine learning diff --git a/docs/settings/reporting-settings.asciidoc b/docs/settings/reporting-settings.asciidoc index 2d91eb07c5236..8c16c76c62569 100644 --- a/docs/settings/reporting-settings.asciidoc +++ b/docs/settings/reporting-settings.asciidoc @@ -195,11 +195,11 @@ a| `xpack.reporting.capture.browser` Defaults to `false`. a| `xpack.reporting.capture.browser` -.chromium.proxy.server` +`.chromium.proxy.server` | The uri for the proxy server. Providing the username and password for the proxy server via the uri is not supported. a| `xpack.reporting.capture.browser` -.chromium.proxy.bypass` +`.chromium.proxy.bypass` | An array of hosts that should not go through the proxy server and should use a direct connection instead. Examples of valid entries are "elastic.co", "*.elastic.co", ".elastic.co", ".elastic.co:5601". diff --git a/docs/user/alerting/alerting-getting-started.asciidoc b/docs/user/alerting/alerting-getting-started.asciidoc index 4eeecad079348..cb2b9b19a0726 100644 --- a/docs/user/alerting/alerting-getting-started.asciidoc +++ b/docs/user/alerting/alerting-getting-started.asciidoc @@ -123,14 +123,15 @@ image::images/alert-concepts-connectors.svg[Connectors provide a central place t [float] === Summary -An _alert_ consists of conditions, _actions_, and a schedule. When conditions are met, _alert instances_ are created that render _actions_ and invoke them. To make action setup and update easier, actions refer to _connectors_ that centralize the information used to connect with {kib} services and third-party integrations. +An _alert_ consists of conditions, _actions_, and a schedule. When conditions are met, _alert instances_ are created that render _actions_ and invoke them. To make action setup and update easier, actions refer to _connectors_ that centralize the information used to connect with {kib} services and third-party integrations. The following example ties these concepts together: image::images/alert-concepts-summary.svg[Alerts, actions, alert instances and connectors work together to convert detection into action] -* *Alert*: a specification of the conditions to be detected, the schedule for detection, and the response when detection occurs. -* *Action*: the response to a detected condition defined in the alert. Typically actions specify a service or third party integration along with alert details that will be sent to it. -* *Alert instance*: state tracked by {kib} for every occurrence of a detected condition. Actions as well as controls like muting and re-notification are controlled at the instance level. -* *Connector*: centralized configurations for services and third party integration that are referenced by actions. +. Anytime an *alert*'s conditions are met, an *alert instance* is created. This example checks for servers with average CPU > 0.9. Three servers meet the condition, so three instances are created. +. Instances create *actions* as long as they are not muted or throttled. When actions are created, the template that was setup in the alert is filled with actual values. In this example three actions are created, and the template string {{server}} is replaced with the server name for each instance. +. {kib} invokes the actions, sending them to a 3rd party *integration* like an email service. +. If the 3rd party integration has connection parameters or credentials, {kib} will fetch these from the *connector* referenced in the action. + [float] [[alerting-concepts-differences]] diff --git a/docs/user/alerting/images/alert-concepts-summary.svg b/docs/user/alerting/images/alert-concepts-summary.svg index 0d63601c0693d..0aed3bf22375f 100644 --- a/docs/user/alerting/images/alert-concepts-summary.svg +++ b/docs/user/alerting/images/alert-concepts-summary.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/user/dashboard/edit-dashboards.asciidoc b/docs/user/dashboard/edit-dashboards.asciidoc index 7b712b355b315..d7f7dc2d65c85 100644 --- a/docs/user/dashboard/edit-dashboards.asciidoc +++ b/docs/user/dashboard/edit-dashboards.asciidoc @@ -81,6 +81,21 @@ Put the dashboard in *Edit* mode, then use the following options: * To delete, open the panel menu, then select *Delete from dashboard*. When you delete a panel from the dashboard, the visualization or saved search from the panel is still available in Kibana. +[float] +[[sync-colors]] +=== Synchronize colors + +By default, dashboard panels that share a non-gradient based color palette will synchronize their color assignment to improve readability. +Color assignment is based on the series name, and the total number of colors is based on the number of unique series names. + +The color synchronizing logic can make the dashboard less readable when there are too many unique series names. It is possible to disable the synchronization behavior: + +. Put the dashboard in *Edit* mode. + +. Click the "Options" button in the top navigation bar. + +. Disable "Sync color palettes across panels". + [float] [[clone-panels]] === Clone panels diff --git a/docs/user/dashboard/url-drilldown.asciidoc b/docs/user/dashboard/url-drilldown.asciidoc index df9fa2dca81fd..b292c1ae5e03f 100644 --- a/docs/user/dashboard/url-drilldown.asciidoc +++ b/docs/user/dashboard/url-drilldown.asciidoc @@ -133,6 +133,12 @@ Example: `{{split event.value ","}}` +|encodeURIComponent +a|Escapes string using built in `encodeURIComponent` function. + +|encodeURIQuery +a|Escapes string using built in `encodeURIComponent` function, while keeping "@", ":", "$", ",", and ";" characters as is. + |=== diff --git a/docs/user/ml/images/ml-data-visualizer-sample.jpg b/docs/user/ml/images/ml-data-visualizer-sample.jpg index ce2bb660d7da1..4d77ef3010c3f 100644 Binary files a/docs/user/ml/images/ml-data-visualizer-sample.jpg and b/docs/user/ml/images/ml-data-visualizer-sample.jpg differ diff --git a/package.json b/package.json index 6e8809063ca57..b657c8273517a 100644 --- a/package.json +++ b/package.json @@ -93,16 +93,16 @@ "**/typescript": "4.1.2" }, "engines": { - "node": "14.15.3", + "node": "14.15.4", "yarn": "^1.21.1" }, "dependencies": { "@babel/core": "^7.11.6", "@babel/runtime": "^7.11.2", "@elastic/datemath": "link:packages/elastic-datemath", - "@elastic/elasticsearch": "7.10.0", + "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@^8.0.0-canary", "@elastic/ems-client": "7.11.0", - "@elastic/eui": "30.6.0", + "@elastic/eui": "31.0.0", "@elastic/filesaver": "1.1.2", "@elastic/good": "^9.0.1-kibana3", "@elastic/node-crypto": "1.2.1", @@ -116,7 +116,7 @@ "@hapi/good-squeeze": "6.0.0", "@hapi/h2o2": "^9.0.2", "@hapi/hapi": "^20.0.3", - "@hapi/hoek": "^9.1.0", + "@hapi/hoek": "^9.1.1", "@hapi/inert": "^6.0.3", "@hapi/podium": "^4.1.1", "@hapi/statehood": "^7.0.3", @@ -824,7 +824,7 @@ "url-loader": "^2.2.0", "use-resize-observer": "^6.0.0", "val-loader": "^1.1.1", - "vega": "^5.17.0", + "vega": "^5.17.3", "vega-lite": "^4.17.0", "vega-schema-url-parser": "^2.1.0", "vega-tooltip": "^0.24.2", diff --git a/packages/kbn-apm-config-loader/src/config.ts b/packages/kbn-apm-config-loader/src/config.ts index 6e5a830d04b17..5e3d52cfd27d1 100644 --- a/packages/kbn-apm-config-loader/src/config.ts +++ b/packages/kbn-apm-config-loader/src/config.ts @@ -153,8 +153,8 @@ export class ApmConfiguration { return { globalLabels: { - branch: process.env.ghprbSourceBranch || '', - targetBranch: process.env.ghprbTargetBranch || '', + branch: process.env.GIT_BRANCH || '', + targetBranch: process.env.PR_TARGET_BRANCH || '', ciBuildNumber: process.env.BUILD_NUMBER || '', isPr: process.env.GITHUB_PR_NUMBER ? true : false, prId: process.env.GITHUB_PR_NUMBER || '', diff --git a/packages/kbn-i18n/GUIDELINE.md b/packages/kbn-i18n/GUIDELINE.md index b7c1371d59ea4..437e73bb27019 100644 --- a/packages/kbn-i18n/GUIDELINE.md +++ b/packages/kbn-i18n/GUIDELINE.md @@ -387,6 +387,50 @@ Splitting sentences into several keys often inadvertently presumes a grammar, a ### Unit tests +#### How to test `FormattedMessage` and `i18n.translate()` components. + +To make `FormattedMessage` component work properly, wrapping it with `I18nProvider` is required. In development/production app, this is done in the ancestor components and developers don't have to worry about that. + +But when unit-testing them, no other component provides that wrapping. That's why `shallowWithI18nProvider` and `mountWithI18nProvider` helpers are created. + +For example, there is a component that has `FormattedMessage` inside, like `SaveModal` component: + +```js +// ... +export const SaveModal = (props) => { + return ( +
+ {/* Other things. */} + + + + {/* More other things. */} +
+ ) +} +``` + +To test `SaveModal` component, it should be wrapped with `I18nProvider` by using `shallowWithI18nProvider`: + +```js +// ... +it('should render normally', async () => { + const component = shallowWithI18nProvider( + + ); + + expect(component).toMatchSnapshot(); +}); +// ... +``` + +If a component uses only `i18n.translate()`, it doesn't need `I18nProvider`. In that case, you can test them with `shallow` and `mount` functions that `enzyme` providers out of the box. + +#### How to test `injectI18n` HOC components. + Testing React component that uses the `injectI18n` higher-order component is more complicated because `injectI18n()` creates a wrapper component around the original component. With shallow rendering only top level component is rendered, that is a wrapper itself, not the original component. Since we want to test the rendering of the original component, we need to access it via the wrapper's `WrappedComponent` property. Its value will be the component we passed into `injectI18n()`. diff --git a/src/core/server/http/prototype_pollution/__snapshots__/validate_object.test.ts.snap b/packages/kbn-std/src/__snapshots__/ensure_no_unsafe_properties.test.ts.snap similarity index 100% rename from src/core/server/http/prototype_pollution/__snapshots__/validate_object.test.ts.snap rename to packages/kbn-std/src/__snapshots__/ensure_no_unsafe_properties.test.ts.snap diff --git a/src/core/server/http/prototype_pollution/validate_object.test.ts b/packages/kbn-std/src/ensure_no_unsafe_properties.test.ts similarity index 89% rename from src/core/server/http/prototype_pollution/validate_object.test.ts rename to packages/kbn-std/src/ensure_no_unsafe_properties.test.ts index 23d6c4ae3b49f..c12626b8d777e 100644 --- a/src/core/server/http/prototype_pollution/validate_object.test.ts +++ b/packages/kbn-std/src/ensure_no_unsafe_properties.test.ts @@ -17,14 +17,14 @@ * under the License. */ -import { validateObject } from './validate_object'; +import { ensureNoUnsafeProperties } from './ensure_no_unsafe_properties'; test(`fails on circular references`, () => { const foo: Record = {}; foo.myself = foo; expect(() => - validateObject({ + ensureNoUnsafeProperties({ payload: foo, }) ).toThrowErrorMatchingInlineSnapshot(`"circular reference detected"`); @@ -57,7 +57,7 @@ test(`fails on circular references`, () => { [property]: value, }; test(`can submit ${JSON.stringify(obj)}`, () => { - expect(() => validateObject(obj)).not.toThrowError(); + expect(() => ensureNoUnsafeProperties(obj)).not.toThrowError(); }); }); }); @@ -74,6 +74,6 @@ test(`fails on circular references`, () => { JSON.parse(`{ "foo": { "bar": { "constructor": { "prototype" : null } } } }`), ].forEach((value) => { test(`can't submit ${JSON.stringify(value)}`, () => { - expect(() => validateObject(value)).toThrowErrorMatchingSnapshot(); + expect(() => ensureNoUnsafeProperties(value)).toThrowErrorMatchingSnapshot(); }); }); diff --git a/src/core/server/http/prototype_pollution/validate_object.ts b/packages/kbn-std/src/ensure_no_unsafe_properties.ts similarity index 97% rename from src/core/server/http/prototype_pollution/validate_object.ts rename to packages/kbn-std/src/ensure_no_unsafe_properties.ts index cab6ce295ce92..47cbea5ecf3ee 100644 --- a/src/core/server/http/prototype_pollution/validate_object.ts +++ b/packages/kbn-std/src/ensure_no_unsafe_properties.ts @@ -31,7 +31,7 @@ const hasOwnProperty = (obj: any, property: string) => const isObject = (obj: any) => typeof obj === 'object' && obj !== null; // we're using a stack instead of recursion so we aren't limited by the call stack -export function validateObject(obj: any) { +export function ensureNoUnsafeProperties(obj: any) { if (!isObject(obj)) { return; } diff --git a/packages/kbn-std/src/index.ts b/packages/kbn-std/src/index.ts index c111428017539..a5b5088f9105f 100644 --- a/packages/kbn-std/src/index.ts +++ b/packages/kbn-std/src/index.ts @@ -27,4 +27,5 @@ export { withTimeout } from './promise'; export { isRelativeUrl, modifyUrl, getUrlOrigin, URLMeaningfulParts } from './url'; export { unset } from './unset'; export { getFlattenedObject } from './get_flattened_object'; +export { ensureNoUnsafeProperties } from './ensure_no_unsafe_properties'; export * from './rxjs_7'; diff --git a/packages/kbn-test/src/functional_test_runner/cli.ts b/packages/kbn-test/src/functional_test_runner/cli.ts index 8f53d6f7cf58b..2dfc9ded66201 100644 --- a/packages/kbn-test/src/functional_test_runner/cli.ts +++ b/packages/kbn-test/src/functional_test_runner/cli.ts @@ -141,22 +141,27 @@ export function runFtrCli() { config: 'test/functional/config.js', }, help: ` - --config=path path to a config file - --bail stop tests after the first failure - --grep pattern used to select which tests to run - --invert invert grep to exclude tests - --include=file a test file to be included, pass multiple times for multiple files - --exclude=file a test file to be excluded, pass multiple times for multiple files - --include-tag=tag a tag to be included, pass multiple times for multiple tags - --exclude-tag=tag a tag to be excluded, pass multiple times for multiple tags - --test-stats print the number of tests (included and excluded) to STDERR - --updateBaselines replace baseline screenshots with whatever is generated from the test - --updateSnapshots replace inline and file snapshots with whatever is generated from the test - -u replace both baseline screenshots and snapshots - --kibana-install-dir directory where the Kibana install being tested resides - --throttle enable network throttling in Chrome browser - --headless run browser in headless mode - `, + --config=path path to a config file + --bail stop tests after the first failure + --grep pattern used to select which tests to run + --invert invert grep to exclude tests + --include=file a test file to be included, pass multiple times for multiple files + --exclude=file a test file to be excluded, pass multiple times for multiple files + --include-tag=tag a tag to be included, pass multiple times for multiple tags. Only + suites which have one of the passed include-tag tags will be executed. + When combined with the --exclude-tag flag both conditions must be met + for a suite to run. + --exclude-tag=tag a tag to be excluded, pass multiple times for multiple tags. Any suite + which has any of the exclude-tags will be excluded. When combined with + the --include-tag flag both conditions must be met for a suite to run. + --test-stats print the number of tests (included and excluded) to STDERR + --updateBaselines replace baseline screenshots with whatever is generated from the test + --updateSnapshots replace inline and file snapshots with whatever is generated from the test + -u replace both baseline screenshots and snapshots + --kibana-install-dir directory where the Kibana install being tested resides + --throttle enable network throttling in Chrome browser + --headless run browser in headless mode + `, }, } ); diff --git a/scripts/ensure_all_tests_in_ci_group.js b/scripts/ensure_all_tests_in_ci_group.js new file mode 100644 index 0000000000000..d189aac8f62e8 --- /dev/null +++ b/scripts/ensure_all_tests_in_ci_group.js @@ -0,0 +1,21 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +require('../src/setup_node_env'); +require('../src/dev/run_ensure_all_tests_in_ci_group'); diff --git a/src/core/public/chrome/ui/header/_index.scss b/src/core/public/chrome/ui/header/_index.scss index 44cd864278325..b11e7e47f4ae7 100644 --- a/src/core/public/chrome/ui/header/_index.scss +++ b/src/core/public/chrome/ui/header/_index.scss @@ -1,5 +1,19 @@ @include euiHeaderAffordForFixed; +.euiDataGrid__restrictBody { + .headerGlobalNav, + .kbnQueryBar { + display: none; + } +} + +.euiDataGrid__restrictBody.euiBody--headerIsFixed { + .euiFlyout { + top: 0; + height: 100%; + } +} + .chrHeaderHelpMenu__version { text-transform: none; } diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index b8843b5c85595..0c65a26b2387b 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -72,6 +72,7 @@ export class DocLinksService { aggs: { date_histogram: `${ELASTICSEARCH_DOCS}search-aggregations-bucket-datehistogram-aggregation.html`, date_range: `${ELASTICSEARCH_DOCS}search-aggregations-bucket-daterange-aggregation.html`, + date_format_pattern: `${ELASTICSEARCH_DOCS}search-aggregations-bucket-daterange-aggregation.html#date-format-pattern`, filter: `${ELASTICSEARCH_DOCS}search-aggregations-bucket-filter-aggregation.html`, filters: `${ELASTICSEARCH_DOCS}search-aggregations-bucket-filters-aggregation.html`, geohash_grid: `${ELASTICSEARCH_DOCS}search-aggregations-bucket-geohashgrid-aggregation.html`, @@ -107,6 +108,7 @@ export class DocLinksService { painless: `${ELASTICSEARCH_DOCS}modules-scripting-painless.html`, painlessApi: `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/painless/${DOC_LINK_VERSION}/painless-api-reference.html`, painlessSyntax: `${ELASTICSEARCH_DOCS}modules-scripting-painless-syntax.html`, + painlessLanguage: `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/painless/${DOC_LINK_VERSION}/painless-lang-spec.html`, luceneExpressions: `${ELASTICSEARCH_DOCS}modules-scripting-expression.html`, }, indexPatterns: { @@ -147,9 +149,10 @@ export class DocLinksService { featureImportance: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-feature-importance.html`, outlierDetectionRoc: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-dfanalytics-evaluate.html#ml-dfanalytics-roc`, regressionEvaluation: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-dfanalytics-evaluate.html#ml-dfanalytics-regression-evaluation`, + classificationAucRoc: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-dfanalytics-evaluate.html#ml-dfanalytics-class-aucroc`, }, transforms: { - guide: `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}/transforms.html`, + guide: `${ELASTICSEARCH_DOCS}transforms.html`, }, visualize: { guide: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/visualize.html`, @@ -157,6 +160,38 @@ export class DocLinksService { lens: `${ELASTIC_WEBSITE_URL}what-is/kibana-lens`, maps: `${ELASTIC_WEBSITE_URL}maps`, }, + observability: { + guide: `${ELASTIC_WEBSITE_URL}guide/en/observability/${DOC_LINK_VERSION}/index.html`, + }, + alerting: { + guide: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/managing-alerts-and-actions.html`, + actionTypes: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/action-types.html`, + }, + maps: { + guide: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/kibana-maps.html`, + }, + monitoring: { + alertsKibana: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/kibana-alerts.html`, + monitorElasticsearch: `${ELASTICSEARCH_DOCS}configuring-metricbeat.html`, + monitorKibana: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/monitoring-metricbeat.html`, + }, + security: { + elasticsearchSettings: `${ELASTICSEARCH_DOCS}security-settings.html`, + kibanaTLS: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/configuring-tls.html`, + kibanaPrivileges: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/kibana-privileges.html`, + indicesPrivileges: `${ELASTICSEARCH_DOCS}security-privileges.html#privileges-list-indices`, + mappingRoles: `${ELASTICSEARCH_DOCS}mapping-roles.html`, + }, + apis: { + createIndex: `${ELASTICSEARCH_DOCS}indices-create-index.html`, + createSnapshotLifecylePolicy: `${ELASTICSEARCH_DOCS}slm-api-put-policy.html`, + createRoleMapping: `${ELASTICSEARCH_DOCS}security-api-put-role-mapping.html`, + createApiKey: `${ELASTICSEARCH_DOCS}security-api-create-api-key.html`, + createPipeline: `${ELASTICSEARCH_DOCS}put-pipeline-api.html`, + createTransformRequest: `${ELASTICSEARCH_DOCS}put-transform.html#put-transform-request-body`, + openIndex: `${ELASTICSEARCH_DOCS}indices-open-close.html`, + updateTransform: `${ELASTICSEARCH_DOCS}update-transform.html`, + }, }, }); } @@ -203,6 +238,7 @@ export interface DocLinksStart { readonly aggs: { readonly date_histogram: string; readonly date_range: string; + readonly date_format_pattern: string; readonly filter: string; readonly filters: string; readonly geohash_grid: string; @@ -263,5 +299,11 @@ export interface DocLinksStart { readonly ml: Record; readonly transforms: Record; readonly visualize: Record; + readonly apis: Record; + readonly observability: Record; + readonly alerting: Record; + readonly maps: Record; + readonly monitoring: Record; + readonly security: Record; }; } diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 0303eb62b6419..4d00ebb213564 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -523,6 +523,7 @@ export interface DocLinksStart { readonly aggs: { readonly date_histogram: string; readonly date_range: string; + readonly date_format_pattern: string; readonly filter: string; readonly filters: string; readonly geohash_grid: string; @@ -583,6 +584,12 @@ export interface DocLinksStart { readonly ml: Record; readonly transforms: Record; readonly visualize: Record; + readonly apis: Record; + readonly observability: Record; + readonly alerting: Record; + readonly maps: Record; + readonly monitoring: Record; + readonly security: Record; }; } diff --git a/src/core/server/http/cookie_session_storage.ts b/src/core/server/http/cookie_session_storage.ts index 1ff0670d78f4e..40bca89c21cb3 100644 --- a/src/core/server/http/cookie_session_storage.ts +++ b/src/core/server/http/cookie_session_storage.ts @@ -19,8 +19,6 @@ import { Request, Server } from '@hapi/hapi'; import hapiAuthCookie from '@hapi/cookie'; -// @ts-expect-error no TS definitions -import Statehood from '@hapi/statehood'; import { KibanaRequest, ensureRawRequest } from './router'; import { SessionStorageFactory, SessionStorage } from './session_storage'; @@ -148,7 +146,7 @@ export async function createCookieSessionStorageFactory( path: basePath === undefined ? '/' : basePath, clearInvalid: false, isHttpOnly: true, - isSameSite: cookieOptions.sameSite === 'None' ? false : cookieOptions.sameSite ?? false, + isSameSite: cookieOptions.sameSite ?? false, }, validateFunc: async (req: Request, session: T | T[]) => { const result = cookieOptions.validate(session); @@ -159,23 +157,6 @@ export async function createCookieSessionStorageFactory( }, }); - // A hack to support SameSite: 'None'. - // Remove it after update Hapi to v19 that supports SameSite: 'None' out of the box. - if (cookieOptions.sameSite === 'None') { - log.debug('Patching Statehood.prepareValue'); - const originalPrepareValue = Statehood.prepareValue; - Statehood.prepareValue = function kibanaStatehoodPrepareValueWrapper( - name: string, - value: unknown, - options: any - ) { - if (name === cookieOptions.name) { - options.isSameSite = cookieOptions.sameSite; - } - return originalPrepareValue(name, value, options); - }; - } - return { asScoped(request: KibanaRequest) { return new ScopedCookieSessionStorage(log, server, ensureRawRequest(request)); diff --git a/src/core/server/http/http_config.ts b/src/core/server/http/http_config.ts index 2bd296fe338ab..61a9b5f04b23f 100644 --- a/src/core/server/http/http_config.ts +++ b/src/core/server/http/http_config.ts @@ -195,7 +195,13 @@ export class HttpConfig { rawExternalUrlConfig: ExternalUrlConfig ) { this.autoListen = rawHttpConfig.autoListen; - this.host = rawHttpConfig.host; + // TODO: Consider dropping support for '0' in v8.0.0. This value is passed + // to hapi, which validates it. Prior to hapi v20, '0' was considered a + // valid host, however the validation logic internally in hapi was + // re-written for v20 and hapi no longer considers '0' a valid host. For + // details, see: + // https://github.com/elastic/kibana/issues/86716#issuecomment-749623781 + this.host = rawHttpConfig.host === '0' ? '0.0.0.0' : rawHttpConfig.host; this.port = rawHttpConfig.port; this.cors = rawHttpConfig.cors; this.customResponseHeaders = Object.entries(rawHttpConfig.customResponseHeaders ?? {}).reduce( diff --git a/src/core/server/http/http_server.ts b/src/core/server/http/http_server.ts index 42e89b66d9c51..81f7c9c45ba50 100644 --- a/src/core/server/http/http_server.ts +++ b/src/core/server/http/http_server.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { Server, ServerRoute } from '@hapi/hapi'; +import { Server } from '@hapi/hapi'; import HapiStaticFiles from '@hapi/inert'; import url from 'url'; import uuid from 'uuid'; @@ -167,6 +167,8 @@ export class HttpServer { for (const router of this.registeredRouters) { for (const route of router.getRoutes()) { this.log.debug(`registering route handler for [${route.path}]`); + // Hapi does not allow payload validation to be specified for 'head' or 'get' requests + const validate = isSafeMethod(route.method) ? undefined : { payload: true }; const { authRequired, tags, body = {}, timeout } = route.options; const { accepts: allow, maxBytes, output, parse } = body; @@ -174,7 +176,7 @@ export class HttpServer { xsrfRequired: route.options.xsrfRequired ?? !isSafeMethod(route.method), }; - const routeOpts: ServerRoute = { + this.server.route({ handler: route.handler, method: route.method, path: route.path, @@ -182,6 +184,11 @@ export class HttpServer { auth: this.getAuthOption(authRequired), app: kibanaRouteOptions, tags: tags ? Array.from(tags) : undefined, + // TODO: This 'validate' section can be removed once the legacy platform is completely removed. + // We are telling Hapi that NP routes can accept any payload, so that it can bypass the default + // validation applied in ./http_tools#getServerOptions + // (All NP routes are already required to specify their own validation in order to access the payload) + validate, // @ts-expect-error Types are outdated and doesn't allow `payload.multipart` to be `true` payload: [allow, maxBytes, output, parse, timeout?.payload].some((x) => x !== undefined) ? { @@ -197,22 +204,7 @@ export class HttpServer { socket: timeout?.idleSocket ?? this.config!.socketTimeout, }, }, - }; - - // Hapi does not allow payload validation to be specified for 'head' or 'get' requests - if (!isSafeMethod(route.method)) { - // TODO: This 'validate' section can be removed once the legacy platform is completely removed. - // We are telling Hapi that NP routes can accept any payload, so that it can bypass the default - // validation applied in ./http_tools#getServerOptions - // (All NP routes are already required to specify their own validation in order to access the payload) - // TODO: Move the setting of the validate option back up to being set at `routeOpts` creation-time once - // https://github.com/hapijs/hoek/pull/365 is merged and released in @hapi/hoek v9.1.1. At that point I - // imagine the ts-error below will go away as well. - // @ts-expect-error "Property 'validate' does not exist on type 'RouteOptions'" <-- ehh?!? yes it does! - routeOpts.options!.validate = { payload: true }; - } - - this.server.route(routeOpts); + }); } } diff --git a/src/core/server/http/http_tools.ts b/src/core/server/http/http_tools.ts index 8bec26f31fa26..f09f3dc2730a1 100644 --- a/src/core/server/http/http_tools.ts +++ b/src/core/server/http/http_tools.ts @@ -29,8 +29,8 @@ import Hoek from '@hapi/hoek'; import type { ServerOptions as TLSOptions } from 'https'; import type { ValidationError } from 'joi'; import uuid from 'uuid'; +import { ensureNoUnsafeProperties } from '@kbn/std'; import { HttpConfig } from './http_config'; -import { validateObject } from './prototype_pollution'; const corsAllowedHeaders = ['Accept', 'Authorization', 'Content-Type', 'If-None-Match', 'kbn-xsrf']; /** @@ -69,7 +69,7 @@ export function getServerOptions(config: HttpConfig, { configureTLS = true } = { // This is a default payload validation which applies to all LP routes which do not specify their own // `validate.payload` handler, in order to reduce the likelyhood of prototype pollution vulnerabilities. // (All NP routes are already required to specify their own validation in order to access the payload) - payload: (value) => Promise.resolve(validateObject(value)), + payload: (value) => Promise.resolve(ensureNoUnsafeProperties(value)), }, }, state: { diff --git a/src/core/server/logging/layouts/json_layout.ts b/src/core/server/logging/layouts/json_layout.ts index 7573d0b837416..34c3c325e7328 100644 --- a/src/core/server/logging/layouts/json_layout.ts +++ b/src/core/server/logging/layouts/json_layout.ts @@ -18,7 +18,7 @@ */ import moment from 'moment-timezone'; -import { merge } from 'lodash'; +import { merge } from '@kbn/std'; import { schema } from '@kbn/config-schema'; import { LogRecord, Layout } from '@kbn/logging'; @@ -53,22 +53,19 @@ export class JsonLayout implements Layout { } public format(record: LogRecord): string { - return JSON.stringify( - merge( - { - '@timestamp': moment(record.timestamp).format('YYYY-MM-DDTHH:mm:ss.SSSZ'), - message: record.message, - error: JsonLayout.errorToSerializableObject(record.error), - log: { - level: record.level.id.toUpperCase(), - logger: record.context, - }, - process: { - pid: record.pid, - }, - }, - record.meta - ) - ); + const log = { + '@timestamp': moment(record.timestamp).format('YYYY-MM-DDTHH:mm:ss.SSSZ'), + message: record.message, + error: JsonLayout.errorToSerializableObject(record.error), + log: { + level: record.level.id.toUpperCase(), + logger: record.context, + }, + process: { + pid: record.pid, + }, + }; + const output = record.meta ? merge(log, record.meta) : log; + return JSON.stringify(output); } } diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/actions.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/actions.test.ts index 05432d65c0558..abda7cf82b121 100644 --- a/src/core/server/saved_objects/migrationsv2/integration_tests/actions.test.ts +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/actions.test.ts @@ -908,7 +908,8 @@ describe('migration actions', () => { }); }); - describe('createIndex', () => { + // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/87160 + describe.skip('createIndex', () => { afterAll(async () => { await client.indices.delete({ index: 'yellow_then_green_index' }); }); diff --git a/src/dev/cli_dev_mode/log.ts b/src/dev/cli_dev_mode/log.ts index f349026ca9cab..3a5d60e65c3f1 100644 --- a/src/dev/cli_dev_mode/log.ts +++ b/src/dev/cli_dev_mode/log.ts @@ -25,7 +25,7 @@ export interface Log { good(label: string, ...args: any[]): void; warn(label: string, ...args: any[]): void; bad(label: string, ...args: any[]): void; - write(label: string, ...args: any[]): void; + write(...args: any[]): void; } export class CliLog implements Log { @@ -58,9 +58,9 @@ export class CliLog implements Log { console.log(Chalk.white.bgRed(` ${label.trim()} `), ...args); } - write(label: string, ...args: any[]) { + write(...args: any[]) { // eslint-disable-next-line no-console - console.log(` ${label.trim()} `, ...args); + console.log(...args); } } @@ -88,10 +88,10 @@ export class TestLog implements Log { }); } - write(label: string, ...args: any[]) { + write(...args: any[]) { this.messages.push({ type: 'write', - args: [label, ...args], + args, }); } } diff --git a/src/dev/cli_dev_mode/optimizer.test.ts b/src/dev/cli_dev_mode/optimizer.test.ts index 8a82012499b33..6017ab2c35d0f 100644 --- a/src/dev/cli_dev_mode/optimizer.test.ts +++ b/src/dev/cli_dev_mode/optimizer.test.ts @@ -191,8 +191,8 @@ it('is ready when optimizer phase is success or issue and logs in familiar forma const lines = await linesPromise; expect(lines).toMatchInlineSnapshot(` Array [ - "np bld log [timestamp] [success][@kbn/optimizer] 0 bundles compiled successfully after 0 sec", - "np bld log [timestamp] [error][@kbn/optimizer] webpack compile errors", + " np bld log [timestamp] [success][@kbn/optimizer] 0 bundles compiled successfully after 0 sec", + " np bld log [timestamp] [error][@kbn/optimizer] webpack compile errors", ] `); }); diff --git a/src/dev/cli_dev_mode/optimizer.ts b/src/dev/cli_dev_mode/optimizer.ts index 9aac414f02b29..f618a0fdbe72f 100644 --- a/src/dev/cli_dev_mode/optimizer.ts +++ b/src/dev/cli_dev_mode/optimizer.ts @@ -105,7 +105,7 @@ export class Optimizer { ToolingLogTextWriter.write( options.writeLogTo ?? process.stdout, - `${dim} log [${time()}] [${level(msg.type)}][${name}] `, + ` ${dim} log [${time()}] [${level(msg.type)}][${name}] `, msg ); return true; diff --git a/tasks/function_test_groups.js b/src/dev/run_ensure_all_tests_in_ci_group.js similarity index 65% rename from tasks/function_test_groups.js rename to src/dev/run_ensure_all_tests_in_ci_group.js index 0b456dcb0da13..b5d36c405cbbb 100644 --- a/tasks/function_test_groups.js +++ b/src/dev/run_ensure_all_tests_in_ci_group.js @@ -21,32 +21,28 @@ import { readFileSync } from 'fs'; import { resolve } from 'path'; import execa from 'execa'; -import grunt from 'grunt'; import { safeLoad } from 'js-yaml'; -const JOBS_YAML = readFileSync(resolve(__dirname, '../.ci/jobs.yml'), 'utf8'); +import { run } from '@kbn/dev-utils'; + +const JOBS_YAML = readFileSync(resolve(__dirname, '../../.ci/jobs.yml'), 'utf8'); const TEST_TAGS = safeLoad(JOBS_YAML) .JOB.filter((id) => id.startsWith('kibana-ciGroup')) .map((id) => id.replace(/^kibana-/, '')); -grunt.registerTask( - 'functionalTests:ensureAllTestsInCiGroup', - 'Check that all of the functional tests are in a CI group', - async function () { - const done = this.async(); - - try { - const result = await execa(process.execPath, [ - 'scripts/functional_test_runner', - ...TEST_TAGS.map((tag) => `--include-tag=${tag}`), - '--config', - 'test/functional/config.js', - '--test-stats', - ]); - const stats = JSON.parse(result.stderr); - - if (stats.excludedTests.length > 0) { - grunt.fail.fatal(` +run(async ({ log }) => { + try { + const result = await execa(process.execPath, [ + 'scripts/functional_test_runner', + ...TEST_TAGS.map((tag) => `--include-tag=${tag}`), + '--config', + 'test/functional/config.js', + '--test-stats', + ]); + const stats = JSON.parse(result.stderr); + + if (stats.excludedTests.length > 0) { + log.error(` ${stats.excludedTests.length} tests are excluded by the ciGroup tags, make sure that all test suites have a "ciGroup{X}" tag and that "tasks/functional_test_groups.js" knows about the tag that you are using. @@ -55,12 +51,11 @@ grunt.registerTask( - ${stats.excludedTests.join('\n - ')} `); - return; - } - - done(); - } catch (error) { - grunt.fail.fatal(error.stack); + process.exitCode = 1; + return; } + } catch (error) { + log.error(error.stack); + process.exitCode = 1; } -); +}); diff --git a/src/dev/run_find_plugins_with_circular_deps.ts b/src/dev/run_find_plugins_with_circular_deps.ts index 1a087e2a01fb2..5afb8df8502df 100644 --- a/src/dev/run_find_plugins_with_circular_deps.ts +++ b/src/dev/run_find_plugins_with_circular_deps.ts @@ -31,10 +31,7 @@ interface Options { type CircularDepList = Set; const allowedList: CircularDepList = new Set([ - 'src/plugins/charts -> src/plugins/discover', - 'src/plugins/charts -> src/plugins/vis_default_editor', 'src/plugins/vis_default_editor -> src/plugins/visualizations', - 'src/plugins/vis_default_editor -> src/plugins/visualize', 'src/plugins/visualizations -> src/plugins/visualize', 'x-pack/plugins/actions -> x-pack/plugins/case', 'x-pack/plugins/case -> x-pack/plugins/security_solution', diff --git a/src/plugins/apm_oss/server/tutorial/index_pattern.json b/src/plugins/apm_oss/server/tutorial/index_pattern.json index b9f3b43b67b84..6eb040f2758af 100644 --- a/src/plugins/apm_oss/server/tutorial/index_pattern.json +++ b/src/plugins/apm_oss/server/tutorial/index_pattern.json @@ -1,7 +1,7 @@ { "attributes": { "fieldFormatMap": "{\"client.bytes\":{\"id\":\"bytes\"},\"client.nat.port\":{\"id\":\"string\"},\"client.port\":{\"id\":\"string\"},\"destination.bytes\":{\"id\":\"bytes\"},\"destination.nat.port\":{\"id\":\"string\"},\"destination.port\":{\"id\":\"string\"},\"event.duration\":{\"id\":\"duration\",\"params\":{\"inputFormat\":\"nanoseconds\",\"outputFormat\":\"asMilliseconds\",\"outputPrecision\":1}},\"event.sequence\":{\"id\":\"string\"},\"event.severity\":{\"id\":\"string\"},\"http.request.body.bytes\":{\"id\":\"bytes\"},\"http.request.bytes\":{\"id\":\"bytes\"},\"http.response.body.bytes\":{\"id\":\"bytes\"},\"http.response.bytes\":{\"id\":\"bytes\"},\"http.response.status_code\":{\"id\":\"string\"},\"log.syslog.facility.code\":{\"id\":\"string\"},\"log.syslog.priority\":{\"id\":\"string\"},\"network.bytes\":{\"id\":\"bytes\"},\"package.size\":{\"id\":\"string\"},\"process.parent.pgid\":{\"id\":\"string\"},\"process.parent.pid\":{\"id\":\"string\"},\"process.parent.ppid\":{\"id\":\"string\"},\"process.parent.thread.id\":{\"id\":\"string\"},\"process.pgid\":{\"id\":\"string\"},\"process.pid\":{\"id\":\"string\"},\"process.ppid\":{\"id\":\"string\"},\"process.thread.id\":{\"id\":\"string\"},\"server.bytes\":{\"id\":\"bytes\"},\"server.nat.port\":{\"id\":\"string\"},\"server.port\":{\"id\":\"string\"},\"source.bytes\":{\"id\":\"bytes\"},\"source.nat.port\":{\"id\":\"string\"},\"source.port\":{\"id\":\"string\"},\"system.cpu.total.norm.pct\":{\"id\":\"percent\"},\"system.memory.actual.free\":{\"id\":\"bytes\"},\"system.memory.total\":{\"id\":\"bytes\"},\"system.process.cgroup.memory.mem.limit.bytes\":{\"id\":\"bytes\"},\"system.process.cgroup.memory.mem.usage.bytes\":{\"id\":\"bytes\"},\"system.process.cpu.total.norm.pct\":{\"id\":\"percent\"},\"system.process.memory.rss.bytes\":{\"id\":\"bytes\"},\"system.process.memory.size\":{\"id\":\"bytes\"},\"url.port\":{\"id\":\"string\"},\"view spans\":{\"id\":\"url\",\"params\":{\"labelTemplate\":\"View Spans\"}}}", - "fields": "[{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"@timestamp\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tags\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.build.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.ephemeral_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.account.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.account.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.availability_zone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.instance.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.instance.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.machine.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.project.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.project.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.provider\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.region\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.image.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.image.tag\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.runtime\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.original_file_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.class\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.data\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.ttl\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.header_flags\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.op_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.class\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.resolved_ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.response_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"ecs.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":4,\"doc_values\":true,\"indexed\":true,\"name\":\"error.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"error.stack_trace\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"error.stack_trace.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.action\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.category\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.created\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.dataset\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.duration\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.end\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.ingested\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.kind\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.module\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"event.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.outcome\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.provider\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.reason\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.risk_score\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.risk_score_norm\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.sequence\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.severity\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.timezone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.url\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.accessed\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.attributes\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.created\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.ctime\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.device\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.directory\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.drive_letter\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.extension\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.gid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.group\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.inode\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mime_type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mode\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mtime\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.owner\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.path.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.original_file_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.target_path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.target_path.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.uid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.alternative_names\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.public_key_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.public_key_curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"file.x509.public_key_exponent\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.public_key_size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.signature_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.version_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.content\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.content.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.method\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.referrer\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.content\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.content.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.status_code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"interface.alias\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"interface.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"interface.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.file.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.level\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.logger\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.file.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.file.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"log.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.facility.code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.facility.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.priority\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.severity.code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.severity.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.application\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.community_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.direction\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.forwarded_ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.iana_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.inner\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.inner.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.inner.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.protocol\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.transport\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.interface.alias\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.interface.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.interface.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.zone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.interface.alias\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.interface.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.interface.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.zone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.vendor\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.build_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.checksum\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.install_scope\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.installed\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.license\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.original_file_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.args\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.args_count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.command_line\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.command_line.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.entity_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.executable\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.executable.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.exit_code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.args\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.args_count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.command_line\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.command_line.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.entity_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.executable\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.executable.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.exit_code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.original_file_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pgid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.ppid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.thread.id\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.thread.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.title\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.title.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.working_directory\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.working_directory.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.original_file_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pgid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.ppid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.thread.id\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.thread.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.title\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.title.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.working_directory\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.working_directory.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.data.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.data.strings\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.data.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.hive\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.key\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.value\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.hosts\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.user\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.author\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.category\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.license\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.ruleset\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.uuid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.ephemeral_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.node.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.state\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.framework\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.cipher\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.certificate\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.certificate_chain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.issuer\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.ja3\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.server_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.subject\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.supported_ciphers\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.alternative_names\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.public_key_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.public_key_curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"tls.client.x509.public_key_exponent\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.public_key_size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.signature_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.version_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.established\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.next_protocol\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.resumed\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.certificate\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.certificate_chain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.issuer\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.ja3s\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.subject\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.alternative_names\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.public_key_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.public_key_curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"tls.server.x509.public_key_exponent\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.public_key_size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.signature_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.version_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.version_protocol\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tracing.span.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tracing.trace.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tracing.transaction.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.extension\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.fragment\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.original.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.password\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.query\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.scheme\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.username\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.device.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.original.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.category\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.classification\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.description.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.enumeration\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.report_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.scanner.vendor\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.base\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.environmental\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.temporal\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.severity\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.alternative_names\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.public_key_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.public_key_curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"x509.public_key_exponent\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.public_key_size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.signature_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.version_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"fields\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"timeseries.instance\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.image.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"docker.container.labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.containerized\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.build\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.codename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.pod.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.pod.uid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.namespace\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.node.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.node.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.labels.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.annotations.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.replicaset.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.deployment.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.statefulset.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.container.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.container.image\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"processor.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"processor.event\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"timestamp.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"enabled\":false,\"indexed\":false,\"name\":\"http.request.headers\",\"scripted\":false,\"searchable\":false},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.finished\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"enabled\":false,\"indexed\":false,\"name\":\"http.response.headers\",\"scripted\":false,\"searchable\":false},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.environment\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.language.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.language.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.runtime.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.runtime.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.framework.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.framework.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.sampled\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.self_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.self_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.breakdown.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"trace.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"parent.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.listening\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.version_major\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"experimental\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.culprit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.grouping_key\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.module\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":4,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.handled\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.level\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.logger_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.param_message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.root\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.subtype\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.self_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.self_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.cpu.total.norm.pct\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.memory.total\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.memory.actual.free\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.cpu.total.norm.pct\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.memory.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.memory.rss.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.cgroup.memory.mem.limit.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.cgroup.memory.mem.usage.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.duration\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.cpu.ns\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.samples.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.alloc_objects.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.alloc_space.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.inuse_objects.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.inuse_space.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.filename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.filename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.service.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.bundle_filepath\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"view spans\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"child.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.action\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.start.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.duration.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.sync\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.db.link\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.db.rows_affected\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.resource\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.message.queue.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.message.age.ms\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.result\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.marks\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.marks.*.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.cls\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.fid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.tbt\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.longtask.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.longtask.sum\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.longtask.max\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.span_count.dropped\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.message.queue.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.message.age.ms\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.histogram\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"metricset.period\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.response_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.response_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_id\",\"scripted\":false,\"searchable\":false,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_index\",\"scripted\":false,\"searchable\":false,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_score\",\"scripted\":false,\"searchable\":false,\"type\":\"number\"}]", + "fields": "[{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"@timestamp\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tags\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.build.original\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.ephemeral_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.organization.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.organization.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.registered_domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.email\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.full_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.account.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.account.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.availability_zone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.instance.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.instance.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.machine.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.project.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.project.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.provider\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.region\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.image.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.image.tag\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.runtime\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.organization.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.registered_domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.email\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.full_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.original_file_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.class\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.data\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.ttl\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.header_flags\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.op_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.class\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.resolved_ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.response_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"ecs.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":4,\"doc_values\":true,\"indexed\":true,\"name\":\"error.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.stack_trace\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.stack_trace.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.type\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.action\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.category\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.created\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.dataset\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.duration\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.end\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.ingested\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.kind\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.module\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"event.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.outcome\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.provider\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.reason\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.risk_score\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.risk_score_norm\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.sequence\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.severity\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.timezone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.url\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.accessed\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.attributes\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.created\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.ctime\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.device\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.directory\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.drive_letter\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.extension\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.gid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.group\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.inode\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mime_type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mode\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mtime\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.owner\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.path\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.path.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.original_file_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.target_path\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.target_path.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.uid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.alternative_names\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.distinguished_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.public_key_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.public_key_curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"file.x509.public_key_exponent\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.public_key_size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.signature_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.distinguished_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.version_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.hostname\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.full\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.email\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.full_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.content\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.content.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.method\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.mime_type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.referrer\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.content\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.content.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.mime_type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.status_code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"interface.alias\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"interface.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"interface.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.file.path\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.level\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.logger\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.file.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.file.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"log.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.facility.code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.facility.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.priority\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.severity.code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.severity.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.application\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.community_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.direction\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.forwarded_ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.iana_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.inner\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.inner.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.inner.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.protocol\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.transport\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.interface.alias\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.interface.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.interface.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.zone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.interface.alias\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.interface.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.interface.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.zone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.full\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.vendor\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.full\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.build_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.checksum\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.install_scope\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.installed\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.license\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.original_file_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.args\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.args_count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.command_line\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.command_line.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.entity_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.executable\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.executable.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.exit_code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.args\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.args_count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.command_line\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.command_line.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.entity_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.executable\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.executable.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.exit_code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.original_file_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pgid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.ppid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.thread.id\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.thread.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.title\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.title.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.working_directory\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.working_directory.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.original_file_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pgid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.ppid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.thread.id\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.thread.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.title\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.title.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.working_directory\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.working_directory.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.data.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.data.strings\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.data.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.hive\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.key\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.path\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.value\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.hosts\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.user\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.author\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.category\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.license\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.ruleset\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.uuid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.organization.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.registered_domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.email\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.full_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.ephemeral_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.node.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.state\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.organization.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.registered_domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.email\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.full_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.framework\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.subtechnique.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.subtechnique.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.subtechnique.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.subtechnique.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.cipher\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.certificate\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.certificate_chain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.issuer\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.ja3\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.server_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.subject\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.supported_ciphers\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.alternative_names\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.distinguished_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.public_key_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.public_key_curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"tls.client.x509.public_key_exponent\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.public_key_size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.signature_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.distinguished_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.version_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.established\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.next_protocol\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.resumed\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.certificate\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.certificate_chain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.issuer\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.ja3s\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.subject\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.alternative_names\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.distinguished_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.public_key_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.public_key_curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"tls.server.x509.public_key_exponent\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.public_key_size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.signature_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.distinguished_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.version_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.version_protocol\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"trace.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.extension\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.fragment\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.full\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.original\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.original.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.password\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.path\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.query\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.registered_domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.scheme\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.username\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.email\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.full_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.email\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.full_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.email\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.full_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.email\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.full_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.device.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.original\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.original.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.full\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.category\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.classification\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.description.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.enumeration\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.report_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.scanner.vendor\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.base\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.environmental\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.temporal\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.severity\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.alternative_names\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.distinguished_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.public_key_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.public_key_curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"x509.public_key_exponent\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.public_key_size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.signature_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.distinguished_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.version_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"fields\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"timeseries.instance\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.image.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"docker.container.labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.containerized\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.build\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.codename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.pod.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.pod.uid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.namespace\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.node.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.node.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.labels.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.annotations.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.replicaset.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.deployment.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.statefulset.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.container.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.container.image\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"processor.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"processor.event\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"timestamp.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"enabled\":false,\"indexed\":false,\"name\":\"http.request.headers\",\"scripted\":false,\"searchable\":false},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.finished\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"enabled\":false,\"indexed\":false,\"name\":\"http.response.headers\",\"scripted\":false,\"searchable\":false},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.environment\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.language.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.language.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.runtime.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.runtime.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.framework.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.framework.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.sampled\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.self_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.self_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.breakdown.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"parent.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.listening\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.version_major\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"experimental\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.culprit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.grouping_key\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.module\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":4,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.handled\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.level\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.logger_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.param_message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.root\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.subtype\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.self_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.self_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.cpu.total.norm.pct\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.memory.total\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.memory.actual.free\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.cpu.total.norm.pct\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.memory.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.memory.rss.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.cgroup.memory.mem.limit.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.cgroup.memory.mem.usage.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.duration\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.cpu.ns\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.samples.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.alloc_objects.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.alloc_space.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.inuse_objects.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.inuse_space.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.filename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.filename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.service.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.bundle_filepath\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"view spans\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"child.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.action\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.start.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.duration.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.sync\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.db.link\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.db.rows_affected\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.resource\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.message.queue.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.message.age.ms\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.result\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.marks\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.marks.*.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.cls\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.fid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.tbt\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.longtask.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.longtask.sum\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.longtask.max\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.span_count.dropped\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.message.queue.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.message.age.ms\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.histogram\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"metricset.period\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.response_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.response_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_id\",\"scripted\":false,\"searchable\":false,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_index\",\"scripted\":false,\"searchable\":false,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_score\",\"scripted\":false,\"searchable\":false,\"type\":\"number\"}]", "sourceFilters": "[{\"value\":\"sourcemap.sourcemap\"}]", "timeFieldName": "@timestamp" }, diff --git a/src/plugins/charts/kibana.json b/src/plugins/charts/kibana.json index a6d4dbba7238f..4510a1ea7d065 100644 --- a/src/plugins/charts/kibana.json +++ b/src/plugins/charts/kibana.json @@ -3,6 +3,5 @@ "version": "kibana", "server": true, "ui": true, - "requiredPlugins": ["expressions"], - "requiredBundles": ["visDefaultEditor"] + "requiredPlugins": ["expressions"] } diff --git a/src/plugins/charts/public/services/mapped_colors/mapped_colors.ts b/src/plugins/charts/public/services/mapped_colors/mapped_colors.ts index 2934d4208d22c..7848cdd3f3140 100644 --- a/src/plugins/charts/public/services/mapped_colors/mapped_colors.ts +++ b/src/plugins/charts/public/services/mapped_colors/mapped_colors.ts @@ -37,15 +37,15 @@ export class MappedColors { private _mapping: any; constructor( - private uiSettings: CoreSetup['uiSettings'], + private uiSettings?: CoreSetup['uiSettings'], private colorPaletteFn: (num: number) => string[] = createColorPalette ) { this._oldMap = {}; this._mapping = {}; } - private getConfigColorMapping() { - return _.mapValues(this.uiSettings.get(COLOR_MAPPING_SETTING), standardizeColor); + private getConfigColorMapping(): Record { + return _.mapValues(this.uiSettings?.get(COLOR_MAPPING_SETTING) || {}, standardizeColor); } public get oldMap(): any { diff --git a/src/plugins/charts/public/services/palettes/palettes.test.tsx b/src/plugins/charts/public/services/palettes/palettes.test.tsx index 5d9337f1ee683..7356f13fddf9f 100644 --- a/src/plugins/charts/public/services/palettes/palettes.test.tsx +++ b/src/plugins/charts/public/services/palettes/palettes.test.tsx @@ -18,9 +18,11 @@ */ import { coreMock } from '../../../../../core/public/mocks'; +import { createColorPalette as createLegacyColorPalette } from '../../../../../../src/plugins/charts/public'; import { PaletteDefinition } from './types'; import { buildPalettes } from './palettes'; import { colorsServiceMock } from '../legacy_colors/mock'; +import { euiPaletteColorBlind, euiPaletteColorBlindBehindText } from '@elastic/eui'; describe('palettes', () => { const palettes: Record = buildPalettes( @@ -28,79 +30,257 @@ describe('palettes', () => { colorsServiceMock ); describe('default palette', () => { - it('should return different colors based on behind text flag', () => { - const palette = palettes.default; + describe('syncColors: false', () => { + it('should return different colors based on behind text flag', () => { + const palette = palettes.default; - const color1 = palette.getColor([ - { - name: 'abc', - rankAtDepth: 0, - totalSeriesAtDepth: 5, - }, - ]); - const color2 = palette.getColor( - [ + const color1 = palette.getColor([ { name: 'abc', rankAtDepth: 0, totalSeriesAtDepth: 5, }, - ], - { - behindText: true, - } - ); - expect(color1).not.toEqual(color2); - }); + ]); + const color2 = palette.getColor( + [ + { + name: 'abc', + rankAtDepth: 0, + totalSeriesAtDepth: 5, + }, + ], + { + behindText: true, + } + ); + expect(color1).not.toEqual(color2); + }); - it('should return different colors based on rank at current series', () => { - const palette = palettes.default; + it('should return different colors based on rank at current series', () => { + const palette = palettes.default; - const color1 = palette.getColor([ - { - name: 'abc', - rankAtDepth: 0, - totalSeriesAtDepth: 5, - }, - ]); - const color2 = palette.getColor([ - { - name: 'abc', - rankAtDepth: 1, - totalSeriesAtDepth: 5, - }, - ]); - expect(color1).not.toEqual(color2); + const color1 = palette.getColor([ + { + name: 'abc', + rankAtDepth: 0, + totalSeriesAtDepth: 5, + }, + ]); + const color2 = palette.getColor([ + { + name: 'abc', + rankAtDepth: 1, + totalSeriesAtDepth: 5, + }, + ]); + expect(color1).not.toEqual(color2); + }); + + it('should return the same color for different positions on outer series layers', () => { + const palette = palettes.default; + + const color1 = palette.getColor([ + { + name: 'abc', + rankAtDepth: 0, + totalSeriesAtDepth: 5, + }, + { + name: 'def', + rankAtDepth: 0, + totalSeriesAtDepth: 2, + }, + ]); + const color2 = palette.getColor([ + { + name: 'abc', + rankAtDepth: 0, + totalSeriesAtDepth: 5, + }, + { + name: 'ghj', + rankAtDepth: 1, + totalSeriesAtDepth: 1, + }, + ]); + expect(color1).toEqual(color2); + }); }); - it('should return the same color for different positions on outer series layers', () => { - const palette = palettes.default; + describe('syncColors: true', () => { + it('should return different colors based on behind text flag', () => { + const palette = palettes.default; - const color1 = palette.getColor([ - { - name: 'abc', - rankAtDepth: 0, - totalSeriesAtDepth: 5, - }, - { - name: 'def', - rankAtDepth: 0, - totalSeriesAtDepth: 2, - }, - ]); - const color2 = palette.getColor([ - { - name: 'abc', - rankAtDepth: 0, - totalSeriesAtDepth: 5, - }, - { - name: 'ghj', - rankAtDepth: 1, - totalSeriesAtDepth: 1, - }, - ]); - expect(color1).toEqual(color2); + const color1 = palette.getColor( + [ + { + name: 'abc', + rankAtDepth: 0, + totalSeriesAtDepth: 5, + }, + ], + { + syncColors: true, + } + ); + const color2 = palette.getColor( + [ + { + name: 'abc', + rankAtDepth: 0, + totalSeriesAtDepth: 5, + }, + ], + { + behindText: true, + syncColors: true, + } + ); + expect(color1).not.toEqual(color2); + }); + + it('should return different colors for different keys', () => { + const palette = palettes.default; + + const color1 = palette.getColor( + [ + { + name: 'abc', + rankAtDepth: 0, + totalSeriesAtDepth: 5, + }, + ], + { + syncColors: true, + } + ); + const color2 = palette.getColor( + [ + { + name: 'def', + rankAtDepth: 0, + totalSeriesAtDepth: 5, + }, + ], + { + syncColors: true, + } + ); + expect(color1).not.toEqual(color2); + }); + + it('should return the same color for the same key, irregardless of rank', () => { + const palette = palettes.default; + + const color1 = palette.getColor( + [ + { + name: 'hij', + rankAtDepth: 0, + totalSeriesAtDepth: 5, + }, + ], + { + syncColors: true, + } + ); + const color2 = palette.getColor( + [ + { + name: 'hij', + rankAtDepth: 5, + totalSeriesAtDepth: 5, + }, + ], + { + syncColors: true, + } + ); + expect(color1).toEqual(color2); + }); + + it('should return the same color for different positions on outer series layers', () => { + const palette = palettes.default; + + const color1 = palette.getColor( + [ + { + name: 'klm', + rankAtDepth: 0, + totalSeriesAtDepth: 5, + }, + { + name: 'def', + rankAtDepth: 0, + totalSeriesAtDepth: 2, + }, + ], + { + syncColors: true, + } + ); + const color2 = palette.getColor( + [ + { + name: 'klm', + rankAtDepth: 3, + totalSeriesAtDepth: 5, + }, + { + name: 'ghj', + rankAtDepth: 1, + totalSeriesAtDepth: 1, + }, + ], + { + syncColors: true, + } + ); + expect(color1).toEqual(color2); + }); + + it('should return the same index of the behind text palette for same key', () => { + const palette = palettes.default; + + const color1 = palette.getColor( + [ + { + name: 'klm', + rankAtDepth: 0, + totalSeriesAtDepth: 5, + }, + { + name: 'def', + rankAtDepth: 0, + totalSeriesAtDepth: 2, + }, + ], + { + syncColors: true, + } + ); + const color2 = palette.getColor( + [ + { + name: 'klm', + rankAtDepth: 3, + totalSeriesAtDepth: 5, + }, + { + name: 'ghj', + rankAtDepth: 1, + totalSeriesAtDepth: 1, + }, + ], + { + syncColors: true, + behindText: true, + } + ); + const color1Index = euiPaletteColorBlind({ rotations: 2 }).indexOf(color1!); + const color2Index = euiPaletteColorBlindBehindText({ rotations: 2 }).indexOf(color2!); + expect(color1Index).toEqual(color2Index); + }); }); }); @@ -136,35 +316,87 @@ describe('palettes', () => { (colorsServiceMock.mappedColors.get as jest.Mock).mockClear(); }); - it('should query legacy color service', () => { - palette.getColor([ - { - name: 'abc', - rankAtDepth: 0, - totalSeriesAtDepth: 10, - }, - ]); - expect(colorsServiceMock.mappedColors.mapKeys).toHaveBeenCalledWith(['abc']); - expect(colorsServiceMock.mappedColors.get).toHaveBeenCalledWith('abc'); + describe('syncColors: false', () => { + it('should not query legacy color service', () => { + palette.getColor( + [ + { + name: 'abc', + rankAtDepth: 0, + totalSeriesAtDepth: 10, + }, + ], + { + syncColors: false, + } + ); + expect(colorsServiceMock.mappedColors.mapKeys).not.toHaveBeenCalled(); + expect(colorsServiceMock.mappedColors.get).not.toHaveBeenCalled(); + }); + + it('should return a color from the legacy palette based on position of first series', () => { + const result = palette.getColor( + [ + { + name: 'abc', + rankAtDepth: 2, + totalSeriesAtDepth: 10, + }, + { + name: 'def', + rankAtDepth: 0, + totalSeriesAtDepth: 10, + }, + ], + { + syncColors: false, + } + ); + expect(result).toEqual(createLegacyColorPalette(20)[2]); + }); }); - it('should always use root series', () => { - palette.getColor([ - { - name: 'abc', - rankAtDepth: 0, - totalSeriesAtDepth: 10, - }, - { - name: 'def', - rankAtDepth: 0, - totalSeriesAtDepth: 10, - }, - ]); - expect(colorsServiceMock.mappedColors.mapKeys).toHaveBeenCalledTimes(1); - expect(colorsServiceMock.mappedColors.mapKeys).toHaveBeenCalledWith(['abc']); - expect(colorsServiceMock.mappedColors.get).toHaveBeenCalledTimes(1); - expect(colorsServiceMock.mappedColors.get).toHaveBeenCalledWith('abc'); + describe('syncColors: true', () => { + it('should query legacy color service', () => { + palette.getColor( + [ + { + name: 'abc', + rankAtDepth: 0, + totalSeriesAtDepth: 10, + }, + ], + { + syncColors: true, + } + ); + expect(colorsServiceMock.mappedColors.mapKeys).toHaveBeenCalledWith(['abc']); + expect(colorsServiceMock.mappedColors.get).toHaveBeenCalledWith('abc'); + }); + + it('should always use root series', () => { + palette.getColor( + [ + { + name: 'abc', + rankAtDepth: 0, + totalSeriesAtDepth: 10, + }, + { + name: 'def', + rankAtDepth: 0, + totalSeriesAtDepth: 10, + }, + ], + { + syncColors: true, + } + ); + expect(colorsServiceMock.mappedColors.mapKeys).toHaveBeenCalledTimes(1); + expect(colorsServiceMock.mappedColors.mapKeys).toHaveBeenCalledWith(['abc']); + expect(colorsServiceMock.mappedColors.get).toHaveBeenCalledTimes(1); + expect(colorsServiceMock.mappedColors.get).toHaveBeenCalledWith('abc'); + }); }); }); diff --git a/src/plugins/charts/public/services/palettes/palettes.tsx b/src/plugins/charts/public/services/palettes/palettes.tsx index c1fd7c3cc739f..ffb237904b36c 100644 --- a/src/plugins/charts/public/services/palettes/palettes.tsx +++ b/src/plugins/charts/public/services/palettes/palettes.tsx @@ -28,26 +28,45 @@ import { euiPaletteNegative, euiPalettePositive, euiPaletteWarm, - euiPaletteColorBlindBehindText, euiPaletteForStatus, euiPaletteForTemperature, euiPaletteComplimentary, + euiPaletteColorBlindBehindText, } from '@elastic/eui'; -import { ChartsPluginSetup } from '../../../../../../src/plugins/charts/public'; +import { flatten, zip } from 'lodash'; +import { + ChartsPluginSetup, + createColorPalette as createLegacyColorPalette, +} from '../../../../../../src/plugins/charts/public'; import { lightenColor } from './lighten_color'; import { ChartColorConfiguration, PaletteDefinition, SeriesLayer } from './types'; import { LegacyColorsService } from '../legacy_colors'; +import { MappedColors } from '../mapped_colors'; function buildRoundRobinCategoricalWithMappedColors(): Omit { const colors = euiPaletteColorBlind({ rotations: 2 }); const behindTextColors = euiPaletteColorBlindBehindText({ rotations: 2 }); + const behindTextColorMap: Record = Object.fromEntries( + zip(colors, behindTextColors) + ); + const mappedColors = new MappedColors(undefined, (num: number) => { + return flatten(new Array(Math.ceil(num / 10)).fill(colors)).map((color) => color.toLowerCase()); + }); function getColor( series: SeriesLayer[], chartConfiguration: ChartColorConfiguration = { behindText: false } ) { - const outputColor = chartConfiguration.behindText - ? behindTextColors[series[0].rankAtDepth % behindTextColors.length] - : colors[series[0].rankAtDepth % colors.length]; + let outputColor: string; + if (chartConfiguration.syncColors) { + const colorKey = series[0].name; + mappedColors.mapKeys([colorKey]); + const mappedColor = mappedColors.get(colorKey); + outputColor = chartConfiguration.behindText ? behindTextColorMap[mappedColor] : mappedColor; + } else { + outputColor = chartConfiguration.behindText + ? behindTextColors[series[0].rankAtDepth % behindTextColors.length] + : colors[series[0].rankAtDepth % colors.length]; + } if (!chartConfiguration.maxDepth || chartConfiguration.maxDepth === 1) { return outputColor; @@ -115,9 +134,15 @@ function buildGradient( function buildSyncedKibanaPalette( colors: ChartsPluginSetup['legacyColors'] ): Omit { + const staticColors = createLegacyColorPalette(20); function getColor(series: SeriesLayer[], chartConfiguration: ChartColorConfiguration = {}) { - colors.mappedColors.mapKeys([series[0].name]); - const outputColor = colors.mappedColors.get(series[0].name); + let outputColor: string; + if (chartConfiguration.syncColors) { + colors.mappedColors.mapKeys([series[0].name]); + outputColor = colors.mappedColors.get(series[0].name); + } else { + outputColor = staticColors[series[0].rankAtDepth % staticColors.length]; + } if (!chartConfiguration.maxDepth || chartConfiguration.maxDepth === 1) { return outputColor; diff --git a/src/plugins/charts/public/services/palettes/types.ts b/src/plugins/charts/public/services/palettes/types.ts index f92bcb4bd0824..15989578518f5 100644 --- a/src/plugins/charts/public/services/palettes/types.ts +++ b/src/plugins/charts/public/services/palettes/types.ts @@ -55,6 +55,11 @@ export interface ChartColorConfiguration { * adjust colors for better a11y. Might be ignored depending on the palette. */ behindText?: boolean; + /** + * Flag whether a color assignment to a given key should be remembered and re-used the next time the key shows up. + * This setting might be ignored based on the palette. + */ + syncColors?: boolean; } /** diff --git a/src/plugins/charts/public/static/components/index.ts b/src/plugins/charts/public/static/components/index.ts index c044d361bed18..0d5d7bf3ba277 100644 --- a/src/plugins/charts/public/static/components/index.ts +++ b/src/plugins/charts/public/static/components/index.ts @@ -17,17 +17,8 @@ * under the License. */ -export { BasicOptions } from './basic_options'; export { ColorMode, LabelRotation, defaultCountLabel } from './collections'; -export { ColorRanges, SetColorRangeValue } from './color_ranges'; -export { ColorSchemaOptions, SetColorSchemaOptionsValue } from './color_schema'; export { ColorSchemaParams, Labels, Style } from './types'; -export { NumberInputOption } from './number_input'; -export { RangeOption } from './range'; -export { RequiredNumberInputOption } from './required_number_input'; -export { SelectOption } from './select'; -export { SwitchOption } from './switch'; -export { TextInputOption } from './text_input'; export { LegendToggle } from './legend_toggle'; export { ColorPicker } from './color_picker'; export { CurrentTime } from './current_time'; diff --git a/src/plugins/dashboard/public/application/dashboard_app_functions.ts b/src/plugins/dashboard/public/application/dashboard_app_functions.ts index 0381fdb2e55b5..af7a485296ea0 100644 --- a/src/plugins/dashboard/public/application/dashboard_app_functions.ts +++ b/src/plugins/dashboard/public/application/dashboard_app_functions.ts @@ -151,6 +151,7 @@ export const getDashboardContainerInput = ({ description: dashboardStateManager.getDescription(), id: dashboardStateManager.savedDashboard.id || '', useMargins: dashboardStateManager.getUseMargins(), + syncColors: dashboardStateManager.getSyncColors(), viewMode: dashboardStateManager.getViewMode(), filters: query.filterManager.getFilters(), query: dashboardStateManager.getQuery(), diff --git a/src/plugins/dashboard/public/application/dashboard_router.tsx b/src/plugins/dashboard/public/application/dashboard_router.tsx index 9673737372478..1ea6355b9c558 100644 --- a/src/plugins/dashboard/public/application/dashboard_router.tsx +++ b/src/plugins/dashboard/public/application/dashboard_router.tsx @@ -22,7 +22,7 @@ import React from 'react'; import { I18nProvider } from '@kbn/i18n/react'; import { parse, ParsedQuery } from 'query-string'; import { render, unmountComponentAtNode } from 'react-dom'; -import { Switch, Route, RouteComponentProps, HashRouter } from 'react-router-dom'; +import { Switch, Route, RouteComponentProps, HashRouter, Redirect } from 'react-router-dom'; import { DashboardListing } from './listing'; import { DashboardApp } from './dashboard_app'; @@ -202,6 +202,9 @@ export async function mountApp({ render={renderDashboard} /> + + + diff --git a/src/plugins/dashboard/public/application/dashboard_state.test.ts b/src/plugins/dashboard/public/application/dashboard_state.test.ts index b07ea762f35e0..f31ed30f8eb80 100644 --- a/src/plugins/dashboard/public/application/dashboard_state.test.ts +++ b/src/plugins/dashboard/public/application/dashboard_state.test.ts @@ -68,6 +68,7 @@ describe('DashboardState', function () { query: {} as DashboardContainerInput['query'], timeRange: {} as DashboardContainerInput['timeRange'], useMargins: true, + syncColors: false, title: 'ultra awesome test dashboard', isFullScreenMode: false, panels: {} as DashboardContainerInput['panels'], diff --git a/src/plugins/dashboard/public/application/dashboard_state_manager.ts b/src/plugins/dashboard/public/application/dashboard_state_manager.ts index daa0bbdfc9f8a..dfcbfcafd3db1 100644 --- a/src/plugins/dashboard/public/application/dashboard_state_manager.ts +++ b/src/plugins/dashboard/public/application/dashboard_state_manager.ts @@ -404,6 +404,15 @@ export class DashboardStateManager { this.stateContainer.transitions.setOption('useMargins', useMargins); } + public getSyncColors() { + // Existing dashboards that don't define this should default to true. + return this.appState.options.syncColors === undefined ? true : this.appState.options.syncColors; + } + + public setSyncColors(syncColors: boolean) { + this.stateContainer.transitions.setOption('syncColors', syncColors); + } + public getHidePanelTitles() { return this.appState.options.hidePanelTitles; } diff --git a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx index 01b4e81fc484c..a3b67ede9f3f9 100644 --- a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx +++ b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx @@ -59,6 +59,7 @@ export interface DashboardContainerInput extends ContainerInput { timeRange: TimeRange; description?: string; useMargins: boolean; + syncColors?: boolean; viewMode: ViewMode; filters: Filter[]; title: string; @@ -93,6 +94,7 @@ export interface InheritedChildInput extends IndexSignature { hidePanelTitles?: boolean; id: string; searchSessionId?: string; + syncColors?: boolean; } export type DashboardReactContextValue = KibanaReactContextValue; @@ -269,6 +271,7 @@ export class DashboardContainer extends Container
{ (async function loadSavedDashboard() { @@ -46,7 +46,7 @@ export const useSavedDashboard = (savedDashboardId: string | undefined, history: pathname: DashboardConstants.CREATE_NEW_DASHBOARD_URL, }); - showWarningToast(getDashboard60Warning()); + toasts.addWarning(getDashboard60Warning()); return; } @@ -63,7 +63,7 @@ export const useSavedDashboard = (savedDashboardId: string | undefined, history: setSavedDashboard(dashboard); } catch (error) { // E.g. a corrupt or deleted dashboard - showDangerToast(error.message); + toasts.addDanger(error.message); history.push(DashboardConstants.LANDING_PAGE_PATH); } })(); @@ -75,8 +75,7 @@ export const useSavedDashboard = (savedDashboardId: string | undefined, history: recentlyAccessedPaths, savedDashboardId, savedDashboards, - showDangerToast, - showWarningToast, + toasts, ]); return savedDashboard; diff --git a/src/plugins/dashboard/public/application/listing/__snapshots__/dashboard_listing.test.tsx.snap b/src/plugins/dashboard/public/application/listing/__snapshots__/dashboard_listing.test.tsx.snap index fad7d8ddaabfe..bce8a661634f6 100644 --- a/src/plugins/dashboard/public/application/listing/__snapshots__/dashboard_listing.test.tsx.snap +++ b/src/plugins/dashboard/public/application/listing/__snapshots__/dashboard_listing.test.tsx.snap @@ -126,7 +126,7 @@ exports[`after fetch When given a title that matches multiple dashboards, filter restrictWidth={true} >
@@ -218,7 +218,7 @@ exports[`after fetch hideWriteControls 1`] = ` restrictWidth={true} >
@@ -358,7 +358,7 @@ exports[`after fetch initialFilter 1`] = ` restrictWidth={true} >
@@ -497,7 +497,7 @@ exports[`after fetch renders all table rows 1`] = ` restrictWidth={true} >
@@ -636,7 +636,7 @@ exports[`after fetch renders call to action when no dashboards exist 1`] = ` restrictWidth={true} >
@@ -775,7 +775,7 @@ exports[`after fetch renders warning when listingLimit is exceeded 1`] = ` restrictWidth={true} >
diff --git a/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx b/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx index 915f245fbcd19..87ccbf29b99f7 100644 --- a/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx +++ b/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx @@ -57,10 +57,12 @@ import { showOptionsPopover } from './show_options_popover'; import { TopNavIds } from './top_nav_ids'; import { ShowShareModal } from './show_share_modal'; import { PanelToolbar } from './panel_toolbar'; +import { OverlayRef } from '../../../../../core/public'; import { DashboardContainer } from '..'; export interface DashboardTopNavState { chromeIsVisible: boolean; + addPanelOverlay?: OverlayRef; savedQuery?: SavedQuery; } @@ -111,14 +113,17 @@ export function DashboardTopNav({ const addFromLibrary = useCallback(() => { if (!isErrorEmbeddable(dashboardContainer)) { - openAddPanelFlyout({ - embeddable: dashboardContainer, - getAllFactories: embeddable.getEmbeddableFactories, - getFactory: embeddable.getEmbeddableFactory, - notifications: core.notifications, - overlays: core.overlays, - SavedObjectFinder: getSavedObjectFinder(core.savedObjects, uiSettings), - }); + setState((s) => ({ + ...s, + addPanelOverlay: openAddPanelFlyout({ + embeddable: dashboardContainer, + getAllFactories: embeddable.getEmbeddableFactories, + getFactory: embeddable.getEmbeddableFactory, + notifications: core.notifications, + overlays: core.overlays, + SavedObjectFinder: getSavedObjectFinder(core.savedObjects, uiSettings), + }), + })); } }, [ embeddable.getEmbeddableFactories, @@ -139,8 +144,16 @@ export function DashboardTopNav({ await factory.create({} as EmbeddableInput, dashboardContainer); }, [dashboardContainer, embeddable]); + const clearAddPanel = useCallback(() => { + if (state.addPanelOverlay) { + state.addPanelOverlay.close(); + setState((s) => ({ ...s, addPanelOverlay: undefined })); + } + }, [state.addPanelOverlay]); + const onChangeViewMode = useCallback( (newMode: ViewMode) => { + clearAddPanel(); const isPageRefresh = newMode === dashboardStateManager.getViewMode(); const isLeavingEditMode = !isPageRefresh && newMode === ViewMode.VIEW; const willLoseChanges = isLeavingEditMode && dashboardStateManager.getIsDirty(timefilter); @@ -178,7 +191,7 @@ export function DashboardTopNav({ } }); }, - [redirectTo, timefilter, core.overlays, savedDashboard.id, dashboardStateManager] + [redirectTo, timefilter, core.overlays, savedDashboard.id, dashboardStateManager, clearAddPanel] ); /** @@ -301,8 +314,16 @@ export function DashboardTopNav({ showCopyOnSave={lastDashboardId ? true : false} /> ); + clearAddPanel(); showSaveModal(dashboardSaveModal, core.i18n.Context); - }, [save, core.i18n.Context, savedObjectsTagging, dashboardStateManager, lastDashboardId]); + }, [ + save, + clearAddPanel, + lastDashboardId, + core.i18n.Context, + savedObjectsTagging, + dashboardStateManager, + ]); const runClone = useCallback(() => { const currentTitle = dashboardStateManager.getTitle(); @@ -348,6 +369,10 @@ export function DashboardTopNav({ onUseMarginsChange: (isChecked: boolean) => { dashboardStateManager.setUseMargins(isChecked); }, + syncColors: dashboardStateManager.getSyncColors(), + onSyncColorsChange: (isChecked: boolean) => { + dashboardStateManager.setSyncColors(isChecked); + }, hidePanelTitles: dashboardStateManager.getHidePanelTitles(), onHidePanelTitlesChange: (isChecked: boolean) => { dashboardStateManager.setHidePanelTitles(isChecked); diff --git a/src/plugins/dashboard/public/application/top_nav/options.tsx b/src/plugins/dashboard/public/application/top_nav/options.tsx index 3398696ff40db..86409cdeba74f 100644 --- a/src/plugins/dashboard/public/application/top_nav/options.tsx +++ b/src/plugins/dashboard/public/application/top_nav/options.tsx @@ -27,17 +27,21 @@ interface Props { onUseMarginsChange: (useMargins: boolean) => void; hidePanelTitles: boolean; onHidePanelTitlesChange: (hideTitles: boolean) => void; + syncColors: boolean; + onSyncColorsChange: (syncColors: boolean) => void; } interface State { useMargins: boolean; hidePanelTitles: boolean; + syncColors: boolean; } export class OptionsMenu extends Component { state = { useMargins: this.props.useMargins, hidePanelTitles: this.props.hidePanelTitles, + syncColors: this.props.syncColors, }; constructor(props: Props) { @@ -56,6 +60,12 @@ export class OptionsMenu extends Component { this.setState({ hidePanelTitles: isChecked }); }; + handleSyncColorsChange = (evt: any) => { + const isChecked = evt.target.checked; + this.props.onSyncColorsChange(isChecked); + this.setState({ syncColors: isChecked }); + }; + render() { return ( @@ -80,6 +90,17 @@ export class OptionsMenu extends Component { data-test-subj="dashboardPanelTitlesCheckbox" /> + + + + ); } diff --git a/src/plugins/dashboard/public/application/top_nav/show_options_popover.tsx b/src/plugins/dashboard/public/application/top_nav/show_options_popover.tsx index 7c23e4808fbea..6c519ccad327f 100644 --- a/src/plugins/dashboard/public/application/top_nav/show_options_popover.tsx +++ b/src/plugins/dashboard/public/application/top_nav/show_options_popover.tsx @@ -39,10 +39,14 @@ export function showOptionsPopover({ onUseMarginsChange, hidePanelTitles, onHidePanelTitlesChange, + syncColors, + onSyncColorsChange, }: { anchorElement: HTMLElement; useMargins: boolean; onUseMarginsChange: (useMargins: boolean) => void; + syncColors: boolean; + onSyncColorsChange: (syncColors: boolean) => void; hidePanelTitles: boolean; onHidePanelTitlesChange: (hideTitles: boolean) => void; }) { @@ -62,6 +66,8 @@ export function showOptionsPopover({ onUseMarginsChange={onUseMarginsChange} hidePanelTitles={hidePanelTitles} onHidePanelTitlesChange={onHidePanelTitlesChange} + syncColors={syncColors} + onSyncColorsChange={onSyncColorsChange} /> diff --git a/src/plugins/dashboard/public/types.ts b/src/plugins/dashboard/public/types.ts index 7e859a81d9d4d..882c5b4286263 100644 --- a/src/plugins/dashboard/public/types.ts +++ b/src/plugins/dashboard/public/types.ts @@ -78,6 +78,7 @@ export interface DashboardAppState { options: { hidePanelTitles: boolean; useMargins: boolean; + syncColors?: boolean; }; query: Query | string; filters: Filter[]; diff --git a/src/plugins/data/public/ui/filter_bar/filter_item.tsx b/src/plugins/data/public/ui/filter_bar/filter_item.tsx index 5e6fd5323c0b7..7b65805a482dd 100644 --- a/src/plugins/data/public/ui/filter_bar/filter_item.tsx +++ b/src/plugins/data/public/ui/filter_bar/filter_item.tsx @@ -71,19 +71,27 @@ export function FilterItem(props: Props) { useEffect(() => { const index = props.filter.meta.index; + let isSubscribed = true; if (index) { getIndexPatterns() .get(index) .then((indexPattern) => { - setIndexPatternExists(!!indexPattern); + if (isSubscribed) { + setIndexPatternExists(!!indexPattern); + } }) .catch(() => { - setIndexPatternExists(false); + if (isSubscribed) { + setIndexPatternExists(false); + } }); - } else { + } else if (isSubscribed) { // Allow filters without an index pattern and don't validate them. setIndexPatternExists(true); } + return () => { + isSubscribed = false; + }; }, [props.filter.meta.index]); function handleBadgeClick(e: MouseEvent) { diff --git a/src/plugins/data/server/kql_telemetry/usage_collector/fetch.test.ts b/src/plugins/data/server/kql_telemetry/usage_collector/fetch.test.ts index 1794df7391cb0..038f340babb1f 100644 --- a/src/plugins/data/server/kql_telemetry/usage_collector/fetch.test.ts +++ b/src/plugins/data/server/kql_telemetry/usage_collector/fetch.test.ts @@ -18,7 +18,7 @@ */ import { fetchProvider } from './fetch'; -import { LegacyAPICaller } from 'kibana/server'; +import { ElasticsearchClient } from 'kibana/server'; import { CollectorFetchContext } from 'src/plugins/usage_collection/server'; import { createCollectorFetchContextMock } from 'src/plugins/usage_collection/server/mocks'; @@ -30,7 +30,7 @@ jest.mock('../../../common', () => ({ })); let fetch: ReturnType; -let callCluster: LegacyAPICaller; +let esClient: ElasticsearchClient; let collectorFetchContext: CollectorFetchContext; const collectorFetchContextMock = createCollectorFetchContextMock(); @@ -38,34 +38,33 @@ function setupMockCallCluster( optCount: { optInCount?: number; optOutCount?: number } | null, language: string | undefined | null ) { - callCluster = (jest.fn((method, params) => { - if (params && 'id' in params && params.id === 'kql-telemetry:kql-telemetry') { - if (optCount === null) { - return Promise.resolve({ + function mockedEsGetMethod() { + if (optCount === null) { + return Promise.resolve({ + body: { _index: '.kibana_1', _id: 'kql-telemetry:kql-telemetry', found: false, - }); - } else { - return Promise.resolve({ + }, + }); + } else { + return Promise.resolve({ + body: { _source: { - 'kql-telemetry': { - ...optCount, - }, + 'kql-telemetry': { ...optCount }, type: 'kql-telemetry', updated_at: '2018-10-05T20:20:56.258Z', }, - }); - } - } else if (params && 'body' in params && params.body.query.term.type === 'config') { - if (language === 'missingConfigDoc') { - return Promise.resolve({ - hits: { - hits: [], - }, - }); - } else { - return Promise.resolve({ + }, + }); + } + } + function mockedEsSearchMethod() { + if (language === 'missingConfigDoc') { + return Promise.resolve({ body: { hits: { hits: [] } } }); + } else { + return Promise.resolve({ + body: { hits: { hits: [ { @@ -77,12 +76,15 @@ function setupMockCallCluster( }, ], }, - }); - } + }, + }); } - - throw new Error('invalid call'); - }) as unknown) as LegacyAPICaller; + } + const esClientMock = ({ + get: jest.fn().mockImplementation(mockedEsGetMethod), + search: jest.fn().mockImplementation(mockedEsSearchMethod), + } as unknown) as ElasticsearchClient; + esClient = esClientMock; } describe('makeKQLUsageCollector', () => { @@ -95,7 +97,7 @@ describe('makeKQLUsageCollector', () => { setupMockCallCluster({ optInCount: 1 }, 'kuery'); collectorFetchContext = { ...collectorFetchContextMock, - callCluster, + esClient, }; const fetchResponse = await fetch(collectorFetchContext); expect(fetchResponse.optInCount).toBe(1); @@ -106,7 +108,7 @@ describe('makeKQLUsageCollector', () => { setupMockCallCluster({ optInCount: 1 }, 'kuery'); collectorFetchContext = { ...collectorFetchContextMock, - callCluster, + esClient, }; const fetchResponse = await fetch(collectorFetchContext); expect(fetchResponse.defaultQueryLanguage).toBe('kuery'); @@ -117,7 +119,7 @@ describe('makeKQLUsageCollector', () => { setupMockCallCluster({ optInCount: 1 }, null); collectorFetchContext = { ...collectorFetchContextMock, - callCluster, + esClient, }; const fetchResponse = await fetch(collectorFetchContext); expect(fetchResponse.defaultQueryLanguage).toBe('lucene'); @@ -127,7 +129,7 @@ describe('makeKQLUsageCollector', () => { setupMockCallCluster({ optInCount: 1 }, undefined); collectorFetchContext = { ...collectorFetchContextMock, - callCluster, + esClient, }; const fetchResponse = await fetch(collectorFetchContext); expect(fetchResponse.defaultQueryLanguage).toBe('default-lucene'); @@ -137,7 +139,7 @@ describe('makeKQLUsageCollector', () => { setupMockCallCluster(null, 'kuery'); collectorFetchContext = { ...collectorFetchContextMock, - callCluster, + esClient, }; const fetchResponse = await fetch(collectorFetchContext); expect(fetchResponse.optInCount).toBe(0); @@ -148,7 +150,7 @@ describe('makeKQLUsageCollector', () => { setupMockCallCluster(null, 'missingConfigDoc'); collectorFetchContext = { ...collectorFetchContextMock, - callCluster, + esClient, }; const fetchResponse = await fetch(collectorFetchContext); expect(fetchResponse.defaultQueryLanguage).toBe('default-lucene'); diff --git a/src/plugins/data/server/kql_telemetry/usage_collector/fetch.ts b/src/plugins/data/server/kql_telemetry/usage_collector/fetch.ts index 21a1843d1ec81..5178aa65705d8 100644 --- a/src/plugins/data/server/kql_telemetry/usage_collector/fetch.ts +++ b/src/plugins/data/server/kql_telemetry/usage_collector/fetch.ts @@ -30,18 +30,22 @@ export interface Usage { } export function fetchProvider(index: string) { - return async ({ callCluster }: CollectorFetchContext): Promise => { - const [response, config] = await Promise.all([ - callCluster('get', { - index, - id: 'kql-telemetry:kql-telemetry', - ignore: [404], - }), - callCluster('search', { - index, - body: { query: { term: { type: 'config' } } }, - ignore: [404], - }), + return async ({ esClient }: CollectorFetchContext): Promise => { + const [{ body: response }, { body: config }] = await Promise.all([ + esClient.get( + { + index, + id: 'kql-telemetry:kql-telemetry', + }, + { ignore: [404] } + ), + esClient.search( + { + index, + body: { query: { term: { type: 'config' } } }, + }, + { ignore: [404] } + ), ]); const queryLanguageConfigValue: string | null | undefined = get( diff --git a/src/plugins/data/server/search/collectors/fetch.ts b/src/plugins/data/server/search/collectors/fetch.ts index 344bc18c7b4b6..9d0d431cf4eaf 100644 --- a/src/plugins/data/server/search/collectors/fetch.ts +++ b/src/plugins/data/server/search/collectors/fetch.ts @@ -20,31 +20,34 @@ import { Observable } from 'rxjs'; import { first } from 'rxjs/operators'; import { SharedGlobalConfig } from 'kibana/server'; +import { SearchResponse } from 'elasticsearch'; import { CollectorFetchContext } from 'src/plugins/usage_collection/server'; import { Usage } from './register'; - -interface SearchTelemetrySavedObject { +interface SearchTelemetry { 'search-telemetry': Usage; } +type ESResponse = SearchResponse; export function fetchProvider(config$: Observable) { - return async ({ callCluster }: CollectorFetchContext): Promise => { + return async ({ esClient }: CollectorFetchContext): Promise => { const config = await config$.pipe(first()).toPromise(); - - const response = await callCluster('search', { - index: config.kibana.index, - body: { - query: { term: { type: { value: 'search-telemetry' } } }, + const { body: esResponse } = await esClient.search( + { + index: config.kibana.index, + body: { + query: { term: { type: { value: 'search-telemetry' } } }, + }, }, - ignore: [404], - }); - - return response.hits.hits.length - ? response.hits.hits[0]._source['search-telemetry'] - : { - successCount: 0, - errorCount: 0, - averageDuration: null, - }; + { ignore: [404] } + ); + const size = esResponse?.hits?.hits?.length ?? 0; + if (!size) { + return { + successCount: 0, + errorCount: 0, + averageDuration: null, + }; + } + return esResponse.hits.hits[0]._source['search-telemetry']; }; } diff --git a/src/plugins/discover/common/index.ts b/src/plugins/discover/common/index.ts index 4334af63539e3..321a102e8d782 100644 --- a/src/plugins/discover/common/index.ts +++ b/src/plugins/discover/common/index.ts @@ -27,4 +27,5 @@ export const FIELDS_LIMIT_SETTING = 'fields:popularLimit'; export const CONTEXT_DEFAULT_SIZE_SETTING = 'context:defaultSize'; export const CONTEXT_STEP_SETTING = 'context:step'; export const CONTEXT_TIE_BREAKER_FIELDS_SETTING = 'context:tieBreakerFields'; +export const DOC_TABLE_LEGACY = 'doc_table:legacy'; export const MODIFY_COLUMNS_ON_SWITCH = 'discover:modifyColumnsOnSwitch'; diff --git a/src/plugins/discover/public/__mocks__/index_pattern.ts b/src/plugins/discover/public/__mocks__/index_pattern.ts index 706118cb71350..f2c12315d4b90 100644 --- a/src/plugins/discover/public/__mocks__/index_pattern.ts +++ b/src/plugins/discover/public/__mocks__/index_pattern.ts @@ -22,29 +22,40 @@ import { IndexPattern } from '../../../data/common'; import { indexPatterns } from '../../../data/public'; const fields = [ + { + name: '_source', + type: '_source', + scripted: false, + filterable: false, + aggregatable: false, + }, { name: '_index', type: 'string', scripted: false, filterable: true, + aggregatable: false, }, { name: 'message', type: 'string', scripted: false, filterable: false, + aggregatable: false, }, { name: 'extension', type: 'string', scripted: false, filterable: true, + aggregatable: true, }, { name: 'bytes', type: 'number', scripted: false, filterable: true, + aggregatable: true, }, { name: 'scripted', @@ -62,16 +73,21 @@ const indexPattern = ({ id: 'the-index-pattern-id', title: 'the-index-pattern-title', metaFields: ['_index', '_score'], + formatField: jest.fn(), flattenHit: undefined, formatHit: jest.fn((hit) => hit._source), fields, - getComputedFields: () => ({}), + getComputedFields: () => ({ docvalueFields: [], scriptFields: {}, storedFields: ['*'] }), getSourceFiltering: () => ({}), getFieldByName: () => ({}), timeFieldName: '', + docvalueFields: [], } as unknown) as IndexPattern; indexPattern.flattenHit = indexPatterns.flattenHitWrapper(indexPattern, indexPattern.metaFields); indexPattern.isTimeBased = () => !!indexPattern.timeFieldName; +indexPattern.formatField = (hit: Record, fieldName: string) => { + return fieldName === '_source' ? hit._source : indexPattern.flattenHit(hit)[fieldName]; +}; export const indexPatternMock = indexPattern; diff --git a/src/plugins/discover/public/application/angular/context_app.html b/src/plugins/discover/public/application/angular/context_app.html index d20b1ca999af9..8dc3e5c87e504 100644 --- a/src/plugins/discover/public/application/angular/context_app.html +++ b/src/plugins/discover/public/application/angular/context_app.html @@ -1,15 +1,3 @@ - - - diff --git a/src/plugins/discover/public/application/angular/context_app.js b/src/plugins/discover/public/application/angular/context_app.js index 145d3afe23224..d9e2452eb8bd6 100644 --- a/src/plugins/discover/public/application/angular/context_app.js +++ b/src/plugins/discover/public/application/angular/context_app.js @@ -56,13 +56,14 @@ getAngularModule().directive('contextApp', function ContextApp() { }); function ContextAppController($scope, Private) { - const { filterManager, indexPatterns, uiSettings } = getServices(); + const { filterManager, indexPatterns, uiSettings, navigation } = getServices(); const queryParameterActions = getQueryParameterActions(filterManager, indexPatterns); const queryActions = Private(QueryActionsProvider); this.state = createInitialState( parseInt(uiSettings.get(CONTEXT_STEP_SETTING), 10), getFirstSortableField(this.indexPattern, uiSettings.get(CONTEXT_TIE_BREAKER_FIELDS_SETTING)) ); + this.topNavMenu = navigation.ui.TopNavMenu; this.actions = _.mapValues( { diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js index 99497d61c716e..639e2212392cc 100644 --- a/src/plugins/discover/public/application/angular/discover.js +++ b/src/plugins/discover/public/application/angular/discover.js @@ -24,7 +24,6 @@ import moment from 'moment'; import dateMath from '@elastic/datemath'; import { i18n } from '@kbn/i18n'; import { createSearchSessionRestorationDataProvider, getState, splitState } from './discover_state'; - import { RequestAdapter } from '../../../../inspector/public'; import { connectToQueryState, @@ -35,6 +34,7 @@ import { import { getSortArray } from './doc_table'; import * as columnActions from './doc_table/actions/columns'; import indexTemplateLegacy from './discover_legacy.html'; +import indexTemplateGrid from './discover_datagrid.html'; import { addHelpMenuToAppChrome } from '../components/help_menu/help_menu_util'; import { discoverResponseHandler } from './response_handler'; import { @@ -124,7 +124,9 @@ app.config(($routeProvider) => { }; const discoverRoute = { ...defaults, - template: indexTemplateLegacy, + template: getServices().uiSettings.get('doc_table:legacy', true) + ? indexTemplateLegacy + : indexTemplateGrid, reloadOnSearch: false, resolve: { savedObjects: function ($route, Promise) { @@ -340,6 +342,8 @@ function discoverController($element, $route, $scope, $timeout, Promise, uiCapab $scope.minimumVisibleRows = 50; $scope.fetchStatus = fetchStatuses.UNINITIALIZED; $scope.showSaveQuery = uiCapabilities.discover.saveQuery; + $scope.showTimeCol = + !config.get('doc_table:hideTimeColumn', false) && $scope.indexPattern.timeFieldName; let abortController; $scope.$on('$destroy', () => { @@ -414,7 +418,7 @@ function discoverController($element, $route, $scope, $timeout, Promise, uiCapab const query = $scope.searchSource.getField('query') || data.query.queryString.getDefaultQuery(); const sort = getSortArray(savedSearch.sort, $scope.indexPattern); - return { + const defaultState = { query, sort: !sort.length ? getDefaultSort($scope.indexPattern, config.get(SORT_DEFAULT_ORDER_SETTING, 'desc')) @@ -427,6 +431,11 @@ function discoverController($element, $route, $scope, $timeout, Promise, uiCapab interval: 'auto', filters: _.cloneDeep($scope.searchSource.getOwnField('filter')), }; + if (savedSearch.grid) { + defaultState.grid = savedSearch.grid; + } + + return defaultState; } $scope.state.index = $scope.indexPattern.id; @@ -440,6 +449,8 @@ function discoverController($element, $route, $scope, $timeout, Promise, uiCapab indexPatternList: $route.current.locals.savedObjects.ip.list, config: config, setHeaderActionMenu: getHeaderActionMenuMounter(), + filterManager, + setAppState, data, }; @@ -783,6 +794,17 @@ function discoverController($element, $route, $scope, $timeout, Promise, uiCapab const columns = columnActions.moveColumn($scope.state.columns, columnName, newIndex); setAppState({ columns }); }; + + $scope.setColumns = function setColumns(columns) { + // remove first element of columns if it's the configured timeFieldName, which is prepended automatically + const actualColumns = + $scope.indexPattern.timeFieldName && $scope.indexPattern.timeFieldName === columns[0] + ? columns.slice(1) + : columns; + $scope.state = { ...$scope.state, columns: actualColumns }; + setAppState({ columns: actualColumns }); + }; + async function setupVisualization() { // If no timefield has been specified we don't create a histogram of messages if (!getTimeField()) return; diff --git a/src/plugins/discover/public/application/angular/discover_datagrid.html b/src/plugins/discover/public/application/angular/discover_datagrid.html new file mode 100644 index 0000000000000..e59ebbb0fafd0 --- /dev/null +++ b/src/plugins/discover/public/application/angular/discover_datagrid.html @@ -0,0 +1,31 @@ + + + + diff --git a/src/plugins/discover/public/application/angular/discover_legacy.html b/src/plugins/discover/public/application/angular/discover_legacy.html index 7cdcd6cbbca3a..3596c0a2519ed 100644 --- a/src/plugins/discover/public/application/angular/discover_legacy.html +++ b/src/plugins/discover/public/application/angular/discover_legacy.html @@ -1,6 +1,5 @@
Hello World
; diff --git a/src/plugins/discover/public/application/components/context_app/context_app_legacy.test.tsx b/src/plugins/discover/public/application/components/context_app/context_app_legacy.test.tsx index f76e0178e98b0..cf6dc70e92d03 100644 --- a/src/plugins/discover/public/application/components/context_app/context_app_legacy.test.tsx +++ b/src/plugins/discover/public/application/components/context_app/context_app_legacy.test.tsx @@ -25,6 +25,7 @@ import { DocTableLegacy } from '../../angular/doc_table/create_doc_table_react'; import { findTestSubject } from '@elastic/eui/lib/test'; import { ActionBar } from '../../angular/context/components/action_bar/action_bar'; import { ContextErrorMessage } from '../context_error_message'; +import { TopNavMenuMock } from './__mocks__/top_nav_menu'; describe('ContextAppLegacy test', () => { const hit = { @@ -64,6 +65,17 @@ describe('ContextAppLegacy test', () => { onChangeSuccessorCount: jest.fn(), predecessorStatus: 'loaded', successorStatus: 'loaded', + topNavMenu: TopNavMenuMock, + }; + const topNavProps = { + appName: 'context', + showSearchBar: true, + showQueryBar: false, + showFilterBar: true, + showSaveQuery: false, + showDatePicker: false, + indexPatterns: [indexPattern], + useDefaultBehaviors: true, }; it('renders correctly', () => { @@ -72,6 +84,9 @@ describe('ContextAppLegacy test', () => { const loadingIndicator = findTestSubject(component, 'contextApp_loadingIndicator'); expect(loadingIndicator.length).toBe(0); expect(component.find(ActionBar).length).toBe(2); + const topNavMenu = component.find(TopNavMenuMock); + expect(topNavMenu.length).toBe(1); + expect(topNavMenu.props()).toStrictEqual(topNavProps); }); it('renders loading indicator', () => { @@ -82,6 +97,7 @@ describe('ContextAppLegacy test', () => { const loadingIndicator = findTestSubject(component, 'contextApp_loadingIndicator'); expect(loadingIndicator.length).toBe(1); expect(component.find(ActionBar).length).toBe(2); + expect(component.find(TopNavMenuMock).length).toBe(1); }); it('renders error message', () => { @@ -90,6 +106,7 @@ describe('ContextAppLegacy test', () => { props.reason = 'something went wrong'; const component = mountWithIntl(); expect(component.find(DocTableLegacy).length).toBe(0); + expect(component.find(TopNavMenuMock).length).toBe(0); const errorMessage = component.find(ContextErrorMessage); expect(errorMessage.length).toBe(1); }); diff --git a/src/plugins/discover/public/application/components/context_app/context_app_legacy.tsx b/src/plugins/discover/public/application/components/context_app/context_app_legacy.tsx index af99c995c60eb..f519df8a0b80d 100644 --- a/src/plugins/discover/public/application/components/context_app/context_app_legacy.tsx +++ b/src/plugins/discover/public/application/components/context_app/context_app_legacy.tsx @@ -27,8 +27,10 @@ import { import { IIndexPattern, IndexPatternField } from '../../../../../data/common/index_patterns'; import { LOADING_STATUS } from './constants'; import { ActionBar, ActionBarProps } from '../../angular/context/components/action_bar/action_bar'; +import { TopNavMenuProps } from '../../../../../navigation/public'; export interface ContextAppProps { + topNavMenu: React.ComponentType; columns: string[]; hits: Array>; indexPattern: IIndexPattern; @@ -96,6 +98,20 @@ export function ContextAppLegacy(renderProps: ContextAppProps) { } as DocTableLegacyProps; }; + const TopNavMenu = renderProps.topNavMenu; + const getNavBarProps = () => { + return { + appName: 'context', + showSearchBar: true, + showQueryBar: false, + showFilterBar: true, + showSaveQuery: false, + showDatePicker: false, + indexPatterns: [renderProps.indexPattern], + useDefaultBehaviors: true, + }; + }; + const loadingFeedback = () => { if (status === LOADING_STATUS.UNINITIALIZED || status === LOADING_STATUS.LOADING) { return ( @@ -112,20 +128,23 @@ export function ContextAppLegacy(renderProps: ContextAppProps) { {isFailed ? ( ) : ( - - - - {loadingFeedback()} - - {isLoaded ? ( -
- -
- ) : null} - - -
-
+
+ + + + + {loadingFeedback()} + + {isLoaded ? ( +
+ +
+ ) : null} + + +
+
+
)} ); diff --git a/src/plugins/discover/public/application/components/context_app/context_app_legacy_directive.ts b/src/plugins/discover/public/application/components/context_app/context_app_legacy_directive.ts index bc4b7c4babd21..dfb5d90c2befe 100644 --- a/src/plugins/discover/public/application/components/context_app/context_app_legacy_directive.ts +++ b/src/plugins/discover/public/application/components/context_app/context_app_legacy_directive.ts @@ -37,5 +37,6 @@ export function createContextAppLegacy(reactDirective: any) { ['successorAvailable', { watchDepth: 'reference' }], ['successorStatus', { watchDepth: 'reference' }], ['onChangeSuccessorCount', { watchDepth: 'reference' }], + ['topNavMenu', { watchDepth: 'reference' }], ]); } diff --git a/src/plugins/discover/public/application/components/create_discover_directive.ts b/src/plugins/discover/public/application/components/create_discover_directive.ts new file mode 100644 index 0000000000000..a146f60652d8a --- /dev/null +++ b/src/plugins/discover/public/application/components/create_discover_directive.ts @@ -0,0 +1,52 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Discover } from './discover'; + +export function createDiscoverDirective(reactDirective: any) { + return reactDirective(Discover, [ + ['fetch', { watchDepth: 'reference' }], + ['fetchCounter', { watchDepth: 'reference' }], + ['fetchError', { watchDepth: 'reference' }], + ['fieldCounts', { watchDepth: 'reference' }], + ['histogramData', { watchDepth: 'reference' }], + ['hits', { watchDepth: 'reference' }], + ['indexPattern', { watchDepth: 'reference' }], + ['onAddColumn', { watchDepth: 'reference' }], + ['onAddFilter', { watchDepth: 'reference' }], + ['onChangeInterval', { watchDepth: 'reference' }], + ['onRemoveColumn', { watchDepth: 'reference' }], + ['onSetColumns', { watchDepth: 'reference' }], + ['onSort', { watchDepth: 'reference' }], + ['opts', { watchDepth: 'reference' }], + ['resetQuery', { watchDepth: 'reference' }], + ['resultState', { watchDepth: 'reference' }], + ['rows', { watchDepth: 'reference' }], + ['searchSource', { watchDepth: 'reference' }], + ['setColumns', { watchDepth: 'reference' }], + ['setIndexPattern', { watchDepth: 'reference' }], + ['showSaveQuery', { watchDepth: 'reference' }], + ['state', { watchDepth: 'reference' }], + ['timefilterUpdateHandler', { watchDepth: 'reference' }], + ['timeRange', { watchDepth: 'reference' }], + ['topNavMenu', { watchDepth: 'reference' }], + ['updateQuery', { watchDepth: 'reference' }], + ['updateSavedQueryId', { watchDepth: 'reference' }], + ]); +} diff --git a/src/plugins/discover/public/application/components/create_discover_grid_directive.tsx b/src/plugins/discover/public/application/components/create_discover_grid_directive.tsx new file mode 100644 index 0000000000000..4e2a2506e282d --- /dev/null +++ b/src/plugins/discover/public/application/components/create_discover_grid_directive.tsx @@ -0,0 +1,56 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import * as React from 'react'; +import { I18nProvider } from '@kbn/i18n/react'; +import { DiscoverGrid, DiscoverGridProps } from './discover_grid/discover_grid'; +import { getServices } from '../../kibana_services'; + +export const DataGridMemoized = React.memo((props: DiscoverGridProps) => ( + +)); + +export function DiscoverGridEmbeddable(props: DiscoverGridProps) { + return ( + + + + ); +} + +/** + * this is just needed for the embeddable + */ +export function createDiscoverGridDirective(reactDirective: any) { + return reactDirective(DiscoverGridEmbeddable, [ + ['columns', { watchDepth: 'collection' }], + ['indexPattern', { watchDepth: 'reference' }], + ['onAddColumn', { watchDepth: 'reference', wrapApply: false }], + ['onFilter', { watchDepth: 'reference', wrapApply: false }], + ['onRemoveColumn', { watchDepth: 'reference', wrapApply: false }], + ['onSetColumns', { watchDepth: 'reference', wrapApply: false }], + ['onSort', { watchDepth: 'reference', wrapApply: false }], + ['rows', { watchDepth: 'collection' }], + ['sampleSize', { watchDepth: 'reference' }], + ['searchDescription', { watchDepth: 'reference' }], + ['searchTitle', { watchDepth: 'reference' }], + ['settings', { watchDepth: 'reference' }], + ['showTimeCol', { watchDepth: 'value' }], + ['sort', { watchDepth: 'value' }], + ]); +} diff --git a/src/plugins/discover/public/application/components/create_discover_legacy_directive.ts b/src/plugins/discover/public/application/components/create_discover_legacy_directive.ts index cb3cb06aa90a3..6e5d47be987d8 100644 --- a/src/plugins/discover/public/application/components/create_discover_legacy_directive.ts +++ b/src/plugins/discover/public/application/components/create_discover_legacy_directive.ts @@ -21,7 +21,6 @@ import { DiscoverLegacy } from './discover_legacy'; export function createDiscoverLegacyDirective(reactDirective: any) { return reactDirective(DiscoverLegacy, [ - ['addColumn', { watchDepth: 'reference' }], ['fetch', { watchDepth: 'reference' }], ['fetchCounter', { watchDepth: 'reference' }], ['fetchError', { watchDepth: 'reference' }], @@ -30,6 +29,7 @@ export function createDiscoverLegacyDirective(reactDirective: any) { ['hits', { watchDepth: 'reference' }], ['indexPattern', { watchDepth: 'reference' }], ['minimumVisibleRows', { watchDepth: 'reference' }], + ['onAddColumn', { watchDepth: 'reference' }], ['onAddFilter', { watchDepth: 'reference' }], ['onChangeInterval', { watchDepth: 'reference' }], ['onMoveColumn', { watchDepth: 'reference' }], diff --git a/src/plugins/discover/public/application/components/discover.scss b/src/plugins/discover/public/application/components/discover.scss index b17da97a45930..665bd98c232a5 100644 --- a/src/plugins/discover/public/application/components/discover.scss +++ b/src/plugins/discover/public/application/components/discover.scss @@ -35,6 +35,10 @@ discover-app { } } +.dscPageContent { + border: $euiBorderThin; +} + .dscPageContent, .dscPageContent__inner { height: 100%; @@ -46,6 +50,7 @@ discover-app { .dscResultCount { padding: $euiSizeS; + min-height: $euiSize * 3; @include euiBreakpoint('xs', 's') { .dscResultCount__toggle { @@ -76,6 +81,13 @@ discover-app { padding: $euiSizeS; } +// new slimmer layout for data grid +.dscHistogramGrid { + display: flex; + height: $euiSize * 8; + padding: $euiSizeS $euiSizeS 0 $euiSizeS; +} + .dscTable { // SASSTODO: add a monospace modifier to the doc-table component .kbnDocTable__row { diff --git a/src/plugins/discover/public/application/components/discover.tsx b/src/plugins/discover/public/application/components/discover.tsx new file mode 100644 index 0000000000000..aa756d960e435 --- /dev/null +++ b/src/plugins/discover/public/application/components/discover.tsx @@ -0,0 +1,321 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import './discover.scss'; +import React, { useState, useRef } from 'react'; +import { + EuiButtonEmpty, + EuiButtonIcon, + EuiFlexGroup, + EuiFlexItem, + EuiHideFor, + EuiHorizontalRule, + EuiPage, + EuiPageBody, + EuiPageContent, + EuiSpacer, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; +import classNames from 'classnames'; +import { HitsCounter } from './hits_counter'; +import { TimechartHeader } from './timechart_header'; +import { getServices } from '../../kibana_services'; +import { DiscoverUninitialized, DiscoverHistogram } from '../angular/directives'; +import { DiscoverNoResults } from './no_results'; +import { LoadingSpinner } from './loading_spinner/loading_spinner'; +import { search } from '../../../../data/public'; +import { + DiscoverSidebarResponsive, + DiscoverSidebarResponsiveProps, +} from './sidebar/discover_sidebar_responsive'; +import { DiscoverProps } from './discover_legacy'; +import { SortPairArr } from '../angular/doc_table/lib/get_sort'; +import { DiscoverGrid, DiscoverGridProps } from './discover_grid/discover_grid'; + +export const SidebarMemoized = React.memo((props: DiscoverSidebarResponsiveProps) => ( + +)); + +export const DataGridMemoized = React.memo((props: DiscoverGridProps) => ( + +)); + +export function Discover({ + fetch, + fetchCounter, + fetchError, + fieldCounts, + histogramData, + hits, + indexPattern, + onAddColumn, + onAddFilter, + onChangeInterval, + onRemoveColumn, + onSetColumns, + onSort, + opts, + resetQuery, + resultState, + rows, + searchSource, + setIndexPattern, + showSaveQuery, + state, + timefilterUpdateHandler, + timeRange, + topNavMenu, + updateQuery, + updateSavedQueryId, +}: DiscoverProps) { + const scrollableDesktop = useRef(null); + const collapseIcon = useRef(null); + const [toggleOn, toggleChart] = useState(true); + const [isSidebarClosed, setIsSidebarClosed] = useState(false); + const services = getServices(); + const { TopNavMenu } = services.navigation.ui; + const { trackUiMetric } = services; + const { savedSearch, indexPatternList, config } = opts; + const bucketAggConfig = opts.chartAggConfigs?.aggs[1]; + const bucketInterval = + bucketAggConfig && search.aggs.isDateHistogramBucketAggConfig(bucketAggConfig) + ? bucketAggConfig.buckets?.getInterval() + : undefined; + const contentCentered = resultState === 'uninitialized'; + const showTimeCol = !config.get('doc_table:hideTimeColumn', false) && indexPattern.timeFieldName; + const columns = + state.columns && + state.columns.length > 0 && + // check if all columns where removed except the configured timeField (this can't be removed) + !(state.columns.length === 1 && state.columns[0] === indexPattern.timeFieldName) + ? state.columns + : ['_source']; + // if columns include _source this is considered as default view, so you can't remove columns + // until you add a column using Discover's sidebar + const defaultColumns = columns.includes('_source'); + + return ( + + + + +

+ {savedSearch.title} +

+ + + + + + + setIsSidebarClosed(!isSidebarClosed)} + data-test-subj="collapseSideBarButton" + aria-controls="discover-sidebar" + aria-expanded={isSidebarClosed ? 'false' : 'true'} + aria-label={i18n.translate('discover.toggleSidebarAriaLabel', { + defaultMessage: 'Toggle sidebar', + })} + buttonRef={collapseIcon} + /> + + + + + {resultState === 'none' && ( + + )} + {resultState === 'uninitialized' && } + {resultState === 'loading' && } + {resultState === 'ready' && ( + + + + + 0 ? hits : 0} + showResetButton={!!(savedSearch && savedSearch.id)} + onResetQuery={resetQuery} + /> + + {toggleOn && ( + + + + )} + + { + toggleChart(!toggleOn); + }} + > + {toggleOn + ? i18n.translate('discover.hideChart', { + defaultMessage: 'Hide chart', + }) + : i18n.translate('discover.showChart', { + defaultMessage: 'Show chart', + })} + + + + + {toggleOn && opts.timefield && ( + +
+ {opts.chartAggConfigs && histogramData && rows.length !== 0 && ( +
+ +
+ )} +
+ +
+ )} + + + + +
+

+ +

+ {rows && rows.length && ( +
+ { + const grid = { ...state.grid } || {}; + const newColumns = { ...grid.columns } || {}; + newColumns[colSettings.columnId] = { + width: colSettings.width, + }; + const newGrid = { ...grid, columns: newColumns }; + opts.setAppState({ grid: newGrid }); + }} + /> +
+ )} +
+
+
+ )} +
+
+
+
+
+
+ ); +} diff --git a/src/plugins/discover/public/application/components/discover_grid/constants.ts b/src/plugins/discover/public/application/components/discover_grid/constants.ts new file mode 100644 index 0000000000000..dec483da8f8a1 --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/constants.ts @@ -0,0 +1,38 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +// data types +export const kibanaJSON = 'kibana-json'; +export const geoPoint = 'geo-point'; +export const unknownType = 'unknown'; +export const gridStyle = { + border: 'all', + fontSize: 's', + cellPadding: 's', + rowHover: 'none', +}; + +export const pageSizeArr = [25, 50, 100]; +export const defaultPageSize = 25; +export const toolbarVisibility = { + showColumnSelector: { + allowHide: false, + allowReorder: true, + }, + showStyleSelector: false, +}; diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid.scss b/src/plugins/discover/public/application/components/discover_grid/discover_grid.scss new file mode 100644 index 0000000000000..64a7eda963349 --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid.scss @@ -0,0 +1,68 @@ +.dscDiscoverGrid { + width: 100%; + max-width: 100%; + height: 100%; + overflow: hidden; + + .euiDataGrid__controls { + border: none; + border-bottom: $euiBorderThin; + } + + .euiDataGridRowCell:first-of-type, + .euiDataGrid--headerShade.euiDataGrid--bordersAll .euiDataGridHeaderCell:first-of-type { + border-left: none; + border-right: none; + } + + .euiDataGridRowCell:last-of-type, + .euiDataGridHeaderCell:last-of-type { + border-right: none; + } +} + +.dscDiscoverGrid__footer { + background-color: $euiColorLightShade; + padding: $euiSize / 2 $euiSize; + margin-top: $euiSize / 4; + text-align: center; +} + +.dscTable__flyoutHeader { + white-space: nowrap; +} + +// We only truncate if the cell is not a control column. +.euiDataGridHeader { + .euiDataGridHeaderCell__content { + @include euiTextTruncate; + overflow: hidden; + white-space: nowrap; + flex-grow: 1; + } + + .euiDataGridHeaderCell__popover { + flex-grow: 0; + flex-basis: auto; + width: auto; + padding-left: $euiSizeXS; + } +} + +.euiDataGridRowCell--numeric { + text-align: right; +} + +.euiDataGrid__noResults { + display: flex; + flex-direction: column; + justify-content: center; + flex: 1 0 100%; + text-align: center; + height: 100%; + width: 100%; +} + +.dscFormatSource { + @include euiTextTruncate; +} diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid.tsx new file mode 100644 index 0000000000000..9588f74ed2bc2 --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid.tsx @@ -0,0 +1,336 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React, { useCallback, useMemo, useState } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import './discover_grid.scss'; +import { + EuiDataGridSorting, + EuiDataGridStyle, + EuiDataGridProps, + EuiDataGrid, + EuiIcon, + EuiScreenReaderOnly, + EuiSpacer, + EuiText, + htmlIdGenerator, +} from '@elastic/eui'; +import { IndexPattern } from '../../../kibana_services'; +import { DocViewFilterFn, ElasticSearchHit } from '../../doc_views/doc_views_types'; +import { getPopoverContents, getSchemaDetectors } from './discover_grid_schema'; +import { DiscoverGridFlyout } from './discover_grid_flyout'; +import { DiscoverGridContext } from './discover_grid_context'; +import { getRenderCellValueFn } from './get_render_cell_value'; +import { DiscoverGridSettings } from './types'; +import { SortPairArr } from '../../angular/doc_table/lib/get_sort'; +import { + getEuiGridColumns, + getLeadControlColumns, + getVisibleColumns, +} from './discover_grid_columns'; +import { defaultPageSize, gridStyle, pageSizeArr, toolbarVisibility } from './constants'; +import { DiscoverServices } from '../../../build_services'; + +interface SortObj { + id: string; + direction: string; +} + +export interface DiscoverGridProps { + /** + * Determines which element labels the grid for ARIA + */ + ariaLabelledBy: string; + /** + * Determines which columns are displayed + */ + columns: string[]; + /** + * Determines whether the given columns are the default ones, so parts of the document + * are displayed (_source) with limited actions (cannor move, remove columns) + * Implemented for matching with legacy behavior + */ + defaultColumns: boolean; + /** + * The used index pattern + */ + indexPattern: IndexPattern; + /** + * Function used to add a column in the document flyout + */ + onAddColumn: (column: string) => void; + /** + * Function to add a filter in the grid cell or document flyout + */ + onFilter: DocViewFilterFn; + /** + * Function used in the grid header and flyout to remove a column + * @param column + */ + onRemoveColumn: (column: string) => void; + /** + * Function triggered when a column is resized by the user + */ + onResize?: (colSettings: { columnId: string; width: number }) => void; + /** + * Function to set all columns + */ + onSetColumns: (columns: string[]) => void; + /** + * function to change sorting of the documents + */ + onSort: (sort: string[][]) => void; + /** + * Array of documents provided by Elasticsearch + */ + rows?: ElasticSearchHit[]; + /** + * The max size of the documents returned by Elasticsearch + */ + sampleSize: number; + /** + * Grid display settings persisted in Elasticsearch (e.g. column width) + */ + settings?: DiscoverGridSettings; + /** + * Saved search description + */ + searchDescription?: string; + /** + * Saved search title + */ + searchTitle?: string; + /** + * Discover plugin services + */ + services: DiscoverServices; + /** + * Determines whether the time columns should be displayed (legacy settings) + */ + showTimeCol: boolean; + /** + * Current sort setting + */ + sort: SortPairArr[]; +} + +export const EuiDataGridMemoized = React.memo((props: EuiDataGridProps) => { + return ; +}); + +export const DiscoverGrid = ({ + ariaLabelledBy, + columns, + defaultColumns, + indexPattern, + onAddColumn, + onFilter, + onRemoveColumn, + onResize, + onSetColumns, + onSort, + rows, + sampleSize, + searchDescription, + searchTitle, + services, + settings, + showTimeCol, + sort, +}: DiscoverGridProps) => { + const [expanded, setExpanded] = useState(undefined); + + /** + * Pagination + */ + const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: defaultPageSize }); + const rowCount = useMemo(() => (rows ? rows.length : 0), [rows]); + const pageCount = useMemo(() => Math.ceil(rowCount / pagination.pageSize), [ + rowCount, + pagination, + ]); + const isOnLastPage = pagination.pageIndex === pageCount - 1; + + const paginationObj = useMemo(() => { + const onChangeItemsPerPage = (pageSize: number) => + setPagination((paginationData) => ({ ...paginationData, pageSize })); + + const onChangePage = (pageIndex: number) => + setPagination((paginationData) => ({ ...paginationData, pageIndex })); + + return { + onChangeItemsPerPage, + onChangePage, + pageIndex: pagination.pageIndex > pageCount - 1 ? 0 : pagination.pageIndex, + pageSize: pagination.pageSize, + pageSizeOptions: pageSizeArr, + }; + }, [pagination, pageCount]); + + /** + * Sorting + */ + const sortingColumns = useMemo(() => sort.map(([id, direction]) => ({ id, direction })), [sort]); + + const onTableSort = useCallback( + (sortingColumnsData) => { + onSort(sortingColumnsData.map(({ id, direction }: SortObj) => [id, direction])); + }, + [onSort] + ); + + /** + * Cell rendering + */ + const renderCellValue = useMemo( + () => + getRenderCellValueFn( + indexPattern, + rows, + rows ? rows.map((hit) => indexPattern.flattenHit(hit)) : [] + ), + [rows, indexPattern] + ); + + /** + * Render variables + */ + const showDisclaimer = rowCount === sampleSize && isOnLastPage; + const randomId = useMemo(() => htmlIdGenerator()(), []); + + const euiGridColumns = useMemo( + () => getEuiGridColumns(columns, settings, indexPattern, showTimeCol, defaultColumns), + [columns, indexPattern, showTimeCol, settings, defaultColumns] + ); + const schemaDetectors = useMemo(() => getSchemaDetectors(), []); + const popoverContents = useMemo(() => getPopoverContents(), []); + const columnsVisibility = useMemo( + () => ({ + visibleColumns: getVisibleColumns(columns, indexPattern, showTimeCol) as string[], + setVisibleColumns: (newColumns: string[]) => { + onSetColumns(newColumns); + }, + }), + [columns, indexPattern, showTimeCol, onSetColumns] + ); + const sorting = useMemo(() => ({ columns: sortingColumns, onSort: onTableSort }), [ + sortingColumns, + onTableSort, + ]); + const lead = useMemo(() => getLeadControlColumns(), []); + + if (!rowCount) { + return ( +
+ + + + + +
+ ); + } + + return ( + + <> + { + if (onResize) { + onResize(col); + } + }} + pagination={paginationObj} + popoverContents={popoverContents} + renderCellValue={renderCellValue} + rowCount={rowCount} + schemaDetectors={schemaDetectors} + sorting={sorting as EuiDataGridSorting} + toolbarVisibility={ + defaultColumns + ? { + ...toolbarVisibility, + showColumnSelector: false, + } + : toolbarVisibility + } + /> + + {showDisclaimer && ( +

+ + + + +

+ )} + {searchTitle && ( + +

+ {searchDescription ? ( + + ) : ( + + )} +

+
+ )} + {expanded && ( + setExpanded(undefined)} + services={services} + /> + )} + +
+ ); +}; diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_cell_actions.test.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_cell_actions.test.tsx new file mode 100644 index 0000000000000..a85583f66c6fa --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_cell_actions.test.tsx @@ -0,0 +1,80 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React from 'react'; +import { mountWithIntl } from '@kbn/test/jest'; +import { findTestSubject } from '@elastic/eui/lib/test'; +import { FilterInBtn, FilterOutBtn } from './discover_grid_cell_actions'; +import { DiscoverGridContext } from './discover_grid_context'; + +import { indexPatternMock } from '../../../__mocks__/index_pattern'; +import { esHits } from '../../../__mocks__/es_hits'; +import { EuiButton } from '@elastic/eui'; + +describe('Discover cell actions ', function () { + it('triggers filter function when FilterInBtn is clicked', async () => { + const contextMock = { + expanded: undefined, + setExpanded: jest.fn(), + rows: esHits, + onFilter: jest.fn(), + indexPattern: indexPatternMock, + isDarkMode: false, + }; + + const component = mountWithIntl( + + } + rowIndex={1} + columnId={'extension'} + isExpanded={false} + closePopover={jest.fn()} + /> + + ); + const button = findTestSubject(component, 'filterForButton'); + await button.simulate('click'); + expect(contextMock.onFilter).toHaveBeenCalledWith('extension', 'jpg', '+'); + }); + it('triggers filter function when FilterOutBtn is clicked', async () => { + const contextMock = { + expanded: undefined, + setExpanded: jest.fn(), + rows: esHits, + onFilter: jest.fn(), + indexPattern: indexPatternMock, + isDarkMode: false, + }; + + const component = mountWithIntl( + + } + rowIndex={1} + columnId={'extension'} + isExpanded={false} + closePopover={jest.fn()} + /> + + ); + const button = findTestSubject(component, 'filterOutButton'); + await button.simulate('click'); + expect(contextMock.onFilter).toHaveBeenCalledWith('extension', 'jpg', '-'); + }); +}); diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_cell_actions.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_cell_actions.tsx new file mode 100644 index 0000000000000..ef56166258c9b --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_cell_actions.tsx @@ -0,0 +1,97 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React, { useContext } from 'react'; +import { EuiDataGridColumnCellActionProps } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { IndexPatternField } from '../../../../../data/common/index_patterns/fields'; +import { DiscoverGridContext } from './discover_grid_context'; + +export const FilterInBtn = ({ + Component, + rowIndex, + columnId, +}: EuiDataGridColumnCellActionProps) => { + const context = useContext(DiscoverGridContext); + const buttonTitle = i18n.translate('discover.grid.filterForAria', { + defaultMessage: 'Filter for this {value}', + values: { value: columnId }, + }); + + return ( + { + const row = context.rows[rowIndex]; + const flattened = context.indexPattern.flattenHit(row); + + if (flattened) { + context.onFilter(columnId, flattened[columnId], '+'); + } + }} + iconType="plusInCircle" + aria-label={buttonTitle} + title={buttonTitle} + data-test-subj="filterForButton" + > + {i18n.translate('discover.grid.filterFor', { + defaultMessage: 'Filter for', + })} + + ); +}; + +export const FilterOutBtn = ({ + Component, + rowIndex, + columnId, +}: EuiDataGridColumnCellActionProps) => { + const context = useContext(DiscoverGridContext); + const buttonTitle = i18n.translate('discover.grid.filterOutAria', { + defaultMessage: 'Filter out this {value}', + values: { value: columnId }, + }); + + return ( + { + const row = context.rows[rowIndex]; + const flattened = context.indexPattern.flattenHit(row); + + if (flattened) { + context.onFilter(columnId, flattened[columnId], '-'); + } + }} + iconType="minusInCircle" + aria-label={buttonTitle} + title={buttonTitle} + data-test-subj="filterOutButton" + > + {i18n.translate('discover.grid.filterOut', { + defaultMessage: 'Filter out', + })} + + ); +}; + +export function buildCellActions(field: IndexPatternField) { + if (!field.aggregatable && !field.searchable) { + return undefined; + } + + return [FilterInBtn, FilterOutBtn]; +} diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.test.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.test.tsx new file mode 100644 index 0000000000000..dad7e1363fdd9 --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.test.tsx @@ -0,0 +1,154 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { indexPatternMock } from '../../../__mocks__/index_pattern'; +import { getEuiGridColumns } from './discover_grid_columns'; +import { indexPatternWithTimefieldMock } from '../../../__mocks__/index_pattern_with_timefield'; + +describe('Discover grid columns ', function () { + it('returns eui grid columns without time column', async () => { + const actual = getEuiGridColumns(['extension', 'message'], {}, indexPatternMock, false, false); + expect(actual).toMatchInlineSnapshot(` + Array [ + Object { + "actions": Object { + "showHide": Object { + "iconType": "cross", + "label": "Remove column", + }, + "showMoveLeft": true, + "showMoveRight": true, + }, + "cellActions": undefined, + "display": undefined, + "id": "extension", + "isSortable": undefined, + "schema": "unknown", + }, + Object { + "actions": Object { + "showHide": Object { + "iconType": "cross", + "label": "Remove column", + }, + "showMoveLeft": true, + "showMoveRight": true, + }, + "cellActions": undefined, + "display": undefined, + "id": "message", + "isSortable": undefined, + "schema": "unknown", + }, + ] + `); + }); + it('returns eui grid columns without time column showing default columns', async () => { + const actual = getEuiGridColumns( + ['extension', 'message'], + {}, + indexPatternWithTimefieldMock, + false, + true + ); + expect(actual).toMatchInlineSnapshot(` + Array [ + Object { + "actions": Object { + "showHide": false, + "showMoveLeft": false, + "showMoveRight": false, + }, + "cellActions": undefined, + "display": undefined, + "id": "extension", + "isSortable": undefined, + "schema": "unknown", + }, + Object { + "actions": Object { + "showHide": false, + "showMoveLeft": false, + "showMoveRight": false, + }, + "cellActions": undefined, + "display": undefined, + "id": "message", + "isSortable": undefined, + "schema": "unknown", + }, + ] + `); + }); + it('returns eui grid columns with time column', async () => { + const actual = getEuiGridColumns( + ['extension', 'message'], + {}, + indexPatternWithTimefieldMock, + true, + false + ); + expect(actual).toMatchInlineSnapshot(` + Array [ + Object { + "actions": Object { + "showHide": false, + "showMoveLeft": true, + "showMoveRight": true, + }, + "cellActions": undefined, + "display": "Time (timestamp)", + "id": "timestamp", + "initialWidth": 180, + "isSortable": undefined, + "schema": "unknown", + }, + Object { + "actions": Object { + "showHide": Object { + "iconType": "cross", + "label": "Remove column", + }, + "showMoveLeft": true, + "showMoveRight": true, + }, + "cellActions": undefined, + "display": undefined, + "id": "extension", + "isSortable": undefined, + "schema": "unknown", + }, + Object { + "actions": Object { + "showHide": Object { + "iconType": "cross", + "label": "Remove column", + }, + "showMoveLeft": true, + "showMoveRight": true, + }, + "cellActions": undefined, + "display": undefined, + "id": "message", + "isSortable": undefined, + "schema": "unknown", + }, + ] + `); + }); +}); diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.tsx new file mode 100644 index 0000000000000..1cf9c84405a61 --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.tsx @@ -0,0 +1,122 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiDataGridColumn, EuiScreenReaderOnly } from '@elastic/eui'; +import { ExpandButton } from './discover_grid_expand_button'; +import { DiscoverGridSettings } from './types'; +import { IndexPattern } from '../../../../../data/common/index_patterns/index_patterns'; +import { buildCellActions } from './discover_grid_cell_actions'; +import { getSchemaByKbnType } from './discover_grid_schema'; + +export function getLeadControlColumns() { + return [ + { + id: 'openDetails', + width: 32, + headerCellRender: () => ( + + + {i18n.translate('discover.controlColumnHeader', { + defaultMessage: 'Control column', + })} + + + ), + rowCellRender: ExpandButton, + }, + ]; +} + +export function buildEuiGridColumn( + columnName: string, + columnWidth: number | undefined = 0, + indexPattern: IndexPattern, + defaultColumns: boolean +) { + const timeString = i18n.translate('discover.timeLabel', { + defaultMessage: 'Time', + }); + const indexPatternField = indexPattern.getFieldByName(columnName); + const column: EuiDataGridColumn = { + id: columnName, + schema: getSchemaByKbnType(indexPatternField?.type), + isSortable: indexPatternField?.sortable, + display: indexPatternField?.displayName, + actions: { + showHide: + defaultColumns || columnName === indexPattern.timeFieldName + ? false + : { + label: i18n.translate('discover.removeColumnLabel', { + defaultMessage: 'Remove column', + }), + iconType: 'cross', + }, + showMoveLeft: !defaultColumns, + showMoveRight: !defaultColumns, + }, + cellActions: indexPatternField ? buildCellActions(indexPatternField) : [], + }; + + if (column.id === indexPattern.timeFieldName) { + column.display = `${timeString} (${indexPattern.timeFieldName})`; + column.initialWidth = 180; + } + if (columnWidth > 0) { + column.initialWidth = Number(columnWidth); + } + return column; +} + +export function getEuiGridColumns( + columns: string[], + settings: DiscoverGridSettings | undefined, + indexPattern: IndexPattern, + showTimeCol: boolean, + defaultColumns: boolean +) { + const timeFieldName = indexPattern.timeFieldName; + const getColWidth = (column: string) => settings?.columns?.[column]?.width ?? 0; + + if (showTimeCol && indexPattern.timeFieldName && !columns.find((col) => col === timeFieldName)) { + const usedColumns = [indexPattern.timeFieldName, ...columns]; + return usedColumns.map((column) => + buildEuiGridColumn(column, getColWidth(column), indexPattern, defaultColumns) + ); + } + + return columns.map((column) => + buildEuiGridColumn(column, getColWidth(column), indexPattern, defaultColumns) + ); +} + +export function getVisibleColumns( + columns: string[], + indexPattern: IndexPattern, + showTimeCol: boolean +) { + const timeFieldName = indexPattern.timeFieldName; + + if (showTimeCol && !columns.find((col) => col === timeFieldName)) { + return [timeFieldName, ...columns]; + } + + return columns; +} diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_context.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_context.tsx new file mode 100644 index 0000000000000..dcc404a0e48df --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_context.tsx @@ -0,0 +1,34 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React from 'react'; +import { DocViewFilterFn, ElasticSearchHit } from '../../doc_views/doc_views_types'; +import { IndexPattern } from '../../../kibana_services'; + +export interface GridContext { + expanded: ElasticSearchHit | undefined; + setExpanded: (hit: ElasticSearchHit | undefined) => void; + rows: ElasticSearchHit[]; + onFilter: DocViewFilterFn; + indexPattern: IndexPattern; + isDarkMode: boolean; +} + +const defaultContext = ({} as unknown) as GridContext; + +export const DiscoverGridContext = React.createContext(defaultContext); diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_expand_button.test.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_expand_button.test.tsx new file mode 100644 index 0000000000000..82fcad8c2cd6f --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_expand_button.test.tsx @@ -0,0 +1,106 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React from 'react'; +import { mountWithIntl } from '@kbn/test/jest'; +import { findTestSubject } from '@elastic/eui/lib/test'; +import { ExpandButton } from './discover_grid_expand_button'; +import { DiscoverGridContext } from './discover_grid_context'; +import { indexPatternMock } from '../../../__mocks__/index_pattern'; +import { esHits } from '../../../__mocks__/es_hits'; + +describe('Discover grid view button ', function () { + it('when no document is expanded, setExpanded is called with current document', async () => { + const contextMock = { + expanded: undefined, + setExpanded: jest.fn(), + rows: esHits, + onFilter: jest.fn(), + indexPattern: indexPatternMock, + isDarkMode: false, + }; + + const component = mountWithIntl( + + + + ); + const button = findTestSubject(component, 'docTableExpandToggleColumn'); + await button.simulate('click'); + expect(contextMock.setExpanded).toHaveBeenCalledWith(esHits[0]); + }); + it('when the current document is expanded, setExpanded is called with undefined', async () => { + const contextMock = { + expanded: esHits[0], + setExpanded: jest.fn(), + rows: esHits, + onFilter: jest.fn(), + indexPattern: indexPatternMock, + isDarkMode: false, + }; + + const component = mountWithIntl( + + + + ); + const button = findTestSubject(component, 'docTableExpandToggleColumn'); + await button.simulate('click'); + expect(contextMock.setExpanded).toHaveBeenCalledWith(undefined); + }); + it('when another document is expanded, setExpanded is called with the current document', async () => { + const contextMock = { + expanded: esHits[0], + setExpanded: jest.fn(), + rows: esHits, + onFilter: jest.fn(), + indexPattern: indexPatternMock, + isDarkMode: false, + }; + + const component = mountWithIntl( + + + + ); + const button = findTestSubject(component, 'docTableExpandToggleColumn'); + await button.simulate('click'); + expect(contextMock.setExpanded).toHaveBeenCalledWith(esHits[1]); + }); +}); diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_expand_button.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_expand_button.tsx new file mode 100644 index 0000000000000..d4a3fe85e34ef --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_expand_button.tsx @@ -0,0 +1,62 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React, { useContext, useEffect } from 'react'; +import { EuiButtonIcon, EuiDataGridCellValueElementProps, EuiToolTip } from '@elastic/eui'; +import themeDark from '@elastic/eui/dist/eui_theme_dark.json'; +import themeLight from '@elastic/eui/dist/eui_theme_light.json'; +import { i18n } from '@kbn/i18n'; +import { DiscoverGridContext } from './discover_grid_context'; +/** + * Button to expand a given row + */ +export const ExpandButton = ({ rowIndex, setCellProps }: EuiDataGridCellValueElementProps) => { + const { expanded, setExpanded, rows, isDarkMode } = useContext(DiscoverGridContext); + const current = rows[rowIndex]; + useEffect(() => { + if (expanded && current && expanded._id === current._id) { + setCellProps({ + style: { + backgroundColor: isDarkMode ? themeDark.euiColorHighlight : themeLight.euiColorHighlight, + }, + }); + } else { + setCellProps({ style: undefined }); + } + }, [expanded, current, setCellProps, isDarkMode]); + + const isCurrentRowExpanded = current === expanded; + const buttonLabel = i18n.translate('discover.grid.viewDoc', { + defaultMessage: 'Toggle dialog with details', + }); + + return ( + + setExpanded(isCurrentRowExpanded ? undefined : current)} + color={isCurrentRowExpanded ? 'primary' : 'subdued'} + iconType={isCurrentRowExpanded ? 'minimize' : 'expand'} + isSelected={isCurrentRowExpanded} + /> + + ); +}; diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_flyout.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_flyout.tsx new file mode 100644 index 0000000000000..79ad98ae2babe --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_flyout.tsx @@ -0,0 +1,143 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutHeader, + EuiTitle, + EuiButtonEmpty, + EuiText, + EuiSpacer, + EuiPortal, +} from '@elastic/eui'; +import { DocViewer } from '../doc_viewer/doc_viewer'; +import { IndexPattern } from '../../../kibana_services'; +import { DocViewFilterFn, ElasticSearchHit } from '../../doc_views/doc_views_types'; +import { DiscoverServices } from '../../../build_services'; +import { getContextUrl } from '../../helpers/get_context_url'; + +interface Props { + columns: string[]; + hit: ElasticSearchHit; + indexPattern: IndexPattern; + onAddColumn: (column: string) => void; + onClose: () => void; + onFilter: DocViewFilterFn; + onRemoveColumn: (column: string) => void; + services: DiscoverServices; +} + +/** + * Flyout displaying an expanded Elasticsearch document + */ +export function DiscoverGridFlyout({ + hit, + indexPattern, + columns, + onFilter, + onClose, + onRemoveColumn, + onAddColumn, + services, +}: Props) { + return ( + + + + +

+ {i18n.translate('discover.grid.tableRow.detailHeading', { + defaultMessage: 'Expanded document', + })} +

+
+ + + + + + + {i18n.translate('discover.grid.tableRow.viewText', { + defaultMessage: 'View:', + })} + + + + + + {i18n.translate('discover.grid.tableRow.viewSingleDocumentLinkTextSimple', { + defaultMessage: 'Single document', + })} + + + {indexPattern.isTimeBased() && indexPattern.id && ( + + + {i18n.translate('discover.grid.tableRow.viewSurroundingDocumentsLinkTextSimple', { + defaultMessage: 'Surrounding documents', + })} + + + )} + +
+ + { + onFilter(mapping, value, mode); + onClose(); + }} + onRemoveColumn={(columnName: string) => { + onRemoveColumn(columnName); + onClose(); + }} + onAddColumn={(columnName: string) => { + onAddColumn(columnName); + onClose(); + }} + /> + +
+
+ ); +} diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_schema.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_schema.tsx new file mode 100644 index 0000000000000..aa87d3982fa06 --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_schema.tsx @@ -0,0 +1,103 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React, { ReactNode } from 'react'; +import { EuiCodeBlock } from '@elastic/eui'; +import { geoPoint, kibanaJSON, unknownType } from './constants'; +import { KBN_FIELD_TYPES } from '../../../../../data/common'; + +export function getSchemaByKbnType(kbnType: string | undefined) { + // Default DataGrid schemas: boolean, numeric, datetime, json, currency, string + switch (kbnType) { + case KBN_FIELD_TYPES.IP: + case KBN_FIELD_TYPES.GEO_SHAPE: + case KBN_FIELD_TYPES.NUMBER: + return 'numeric'; + case KBN_FIELD_TYPES.BOOLEAN: + return 'boolean'; + case KBN_FIELD_TYPES.STRING: + return 'string'; + case KBN_FIELD_TYPES.DATE: + return 'datetime'; + case KBN_FIELD_TYPES._SOURCE: + return kibanaJSON; + case KBN_FIELD_TYPES.GEO_POINT: + return geoPoint; + default: + return unknownType; + } +} + +export function getSchemaDetectors() { + return [ + { + type: kibanaJSON, + detector() { + return 0; // this schema is always explicitly defined + }, + sortTextAsc: '', + sortTextDesc: '', + icon: '', + color: '', + }, + { + type: unknownType, + detector() { + return 0; // this schema is always explicitly defined + }, + sortTextAsc: '', + sortTextDesc: '', + icon: '', + color: '', + }, + { + type: geoPoint, + detector() { + return 0; // this schema is always explicitly defined + }, + sortTextAsc: '', + sortTextDesc: '', + icon: 'tokenGeo', + }, + ]; +} + +/** + * Returns custom popover content for certain schemas + */ +export function getPopoverContents() { + return { + [geoPoint]: ({ children }: { children: ReactNode }) => { + return {children}; + }, + [unknownType]: ({ children }: { children: ReactNode }) => { + return ( + + {children} + + ); + }, + [kibanaJSON]: ({ children }: { children: ReactNode }) => { + return ( + + {children} + + ); + }, + }; +} diff --git a/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.test.tsx b/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.test.tsx new file mode 100644 index 0000000000000..d9896f4c53907 --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.test.tsx @@ -0,0 +1,132 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React from 'react'; +import { shallow } from 'enzyme'; +import { getRenderCellValueFn } from './get_render_cell_value'; +import { indexPatternMock } from '../../../__mocks__/index_pattern'; +const rows = [ + { + _id: '1', + _index: 'test', + _type: 'test', + _score: 1, + _source: { bytes: 100 }, + }, +]; + +describe('Discover grid cell rendering', function () { + it('renders bytes column correctly', () => { + const DiscoverGridCellValue = getRenderCellValueFn( + indexPatternMock, + rows, + rows.map((row) => indexPatternMock.flattenHit(row)) + ); + const component = shallow( + + ); + expect(component.html()).toMatchInlineSnapshot(`"100"`); + }); + it('renders _source column correctly', () => { + const DiscoverGridCellValue = getRenderCellValueFn( + indexPatternMock, + rows, + rows.map((row) => indexPatternMock.flattenHit(row)) + ); + const component = shallow( + + ); + expect(component.html()).toMatchInlineSnapshot( + `"
bytes
100
"` + ); + }); + + it('renders _source column correctly when isDetails is set to true', () => { + const DiscoverGridCellValue = getRenderCellValueFn( + indexPatternMock, + rows, + rows.map((row) => indexPatternMock.flattenHit(row)) + ); + const component = shallow( + + ); + expect(component.html()).toMatchInlineSnapshot(` + "{ + "bytes": 100 + }" + `); + }); + + it('renders correctly when invalid row is given', () => { + const DiscoverGridCellValue = getRenderCellValueFn( + indexPatternMock, + rows, + rows.map((row) => indexPatternMock.flattenHit(row)) + ); + const component = shallow( + + ); + expect(component.html()).toMatchInlineSnapshot(`"-"`); + }); + it('renders correctly when invalid column is given', () => { + const DiscoverGridCellValue = getRenderCellValueFn( + indexPatternMock, + rows, + rows.map((row) => indexPatternMock.flattenHit(row)) + ); + const component = shallow( + + ); + expect(component.html()).toMatchInlineSnapshot(`"-"`); + }); +}); diff --git a/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.tsx b/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.tsx new file mode 100644 index 0000000000000..2157e778f84db --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.tsx @@ -0,0 +1,116 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React, { Fragment, useContext, useEffect } from 'react'; +import { i18n } from '@kbn/i18n'; +import themeLight from '@elastic/eui/dist/eui_theme_light.json'; +import themeDark from '@elastic/eui/dist/eui_theme_dark.json'; + +import { + EuiDataGridCellValueElementProps, + EuiDescriptionList, + EuiDescriptionListTitle, + EuiDescriptionListDescription, +} from '@elastic/eui'; +import { IndexPattern } from '../../../kibana_services'; +import { ElasticSearchHit } from '../../doc_views/doc_views_types'; +import { DiscoverGridContext } from './discover_grid_context'; + +export const getRenderCellValueFn = ( + indexPattern: IndexPattern, + rows: ElasticSearchHit[] | undefined, + rowsFlattened: Array> +) => ({ rowIndex, columnId, isDetails, setCellProps }: EuiDataGridCellValueElementProps) => { + const row = rows ? (rows[rowIndex] as Record) : undefined; + const rowFlattened = rowsFlattened + ? (rowsFlattened[rowIndex] as Record) + : undefined; + + const field = indexPattern.fields.getByName(columnId); + const ctx = useContext(DiscoverGridContext); + + useEffect(() => { + if (ctx.expanded && row && ctx.expanded._id === row._id) { + setCellProps({ + style: { + backgroundColor: ctx.isDarkMode + ? themeDark.euiColorHighlight + : themeLight.euiColorHighlight, + }, + }); + } else { + setCellProps({ style: undefined }); + } + }, [ctx, row, setCellProps]); + + if (typeof row === 'undefined' || typeof rowFlattened === 'undefined') { + return -; + } + + if (field && field.type === '_source') { + if (isDetails) { + // nicely formatted JSON for the expanded view + return {JSON.stringify(row[columnId], null, 2)}; + } + const formatted = indexPattern.formatHit(row); + + return ( + + {Object.keys(formatted).map((key) => ( + + {key} + + + ))} + + ); + } + + if (!field?.type && rowFlattened && typeof rowFlattened[columnId] === 'object') { + if (isDetails) { + // nicely formatted JSON for the expanded view + return {JSON.stringify(rowFlattened[columnId], null, 2)}; + } + + return {JSON.stringify(rowFlattened[columnId])}; + } + + if (field?.type === 'geo_point' && rowFlattened && rowFlattened[columnId]) { + const valueFormatted = rowFlattened[columnId] as { lat: number; lon: number }; + return ( +
+ {i18n.translate('discover.latitudeAndLongitude', { + defaultMessage: 'Lat: {lat} Lon: {lon}', + values: { + lat: valueFormatted?.lat, + lon: valueFormatted?.lon, + }, + })} +
+ ); + } + + const valueFormatted = indexPattern.formatField(row, columnId); + if (typeof valueFormatted === 'undefined') { + return -; + } + return ( + // eslint-disable-next-line react/no-danger + + ); +}; diff --git a/src/plugins/discover/public/application/components/discover_grid/types.ts b/src/plugins/discover/public/application/components/discover_grid/types.ts new file mode 100644 index 0000000000000..3d57dbffe924e --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/types.ts @@ -0,0 +1,29 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * User configurable state of data grid, persisted in saved search + */ +export interface DiscoverGridSettings { + columns?: Record; +} + +export interface DiscoverGridSettingsColumn { + width?: number; +} diff --git a/src/plugins/discover/public/application/components/discover_legacy.test.tsx b/src/plugins/discover/public/application/components/discover_legacy.test.tsx index e2f4ba7ab6e2e..bad5c1d2e532d 100644 --- a/src/plugins/discover/public/application/components/discover_legacy.test.tsx +++ b/src/plugins/discover/public/application/components/discover_legacy.test.tsx @@ -67,7 +67,6 @@ function getProps(indexPattern: IndexPattern) { } as unknown) as DiscoverServices; return { - addColumn: jest.fn(), fetch: jest.fn(), fetchCounter: 0, fetchError: undefined, @@ -75,6 +74,7 @@ function getProps(indexPattern: IndexPattern) { hits: esHits.length, indexPattern, minimumVisibleRows: 10, + onAddColumn: jest.fn(), onAddFilter: jest.fn(), onChangeInterval: jest.fn(), onMoveColumn: jest.fn(), diff --git a/src/plugins/discover/public/application/components/discover_legacy.tsx b/src/plugins/discover/public/application/components/discover_legacy.tsx index d228be66990bd..436a145024437 100644 --- a/src/plugins/discover/public/application/components/discover_legacy.tsx +++ b/src/plugins/discover/public/application/components/discover_legacy.tsx @@ -63,46 +63,161 @@ import { import { DocViewFilterFn, ElasticSearchHit } from '../doc_views/doc_views_types'; export interface DiscoverProps { - addColumn: (column: string) => void; + /** + * Function to fetch documents from Elasticsearch + */ fetch: () => void; + /** + * Counter how often data was fetched (used for testing) + */ fetchCounter: number; + /** + * Error in case of a failing document fetch + */ fetchError?: Error; + /** + * Statistics by fields calculated using the fetched documents + */ fieldCounts: Record; + /** + * Histogram aggregation data + */ histogramData?: Chart; + /** + * Number of documents found by recent fetch + */ hits: number; + /** + * Current IndexPattern + */ indexPattern: IndexPattern; + /** + * Value needed for legacy "infinite" loading functionality + * Determins how much records are rendered using the legacy table + * Increased when scrolling down + */ minimumVisibleRows: number; + /** + * Function to add a column to state + */ + onAddColumn: (column: string) => void; + /** + * Function to add a filter to state + */ onAddFilter: DocViewFilterFn; + /** + * Function to change the used time interval of the date histogram + */ onChangeInterval: (interval: string) => void; + /** + * Function to move a given column to a given index, used in legacy table + */ onMoveColumn: (columns: string, newIdx: number) => void; + /** + * Function to remove a given column from state + */ onRemoveColumn: (column: string) => void; + /** + * Function to replace columns in state + */ onSetColumns: (columns: string[]) => void; + /** + * Function to scroll down the legacy table to the bottom + */ onSkipBottomButtonClick: () => void; + /** + * Function to change sorting of the table, triggers a fetch + */ onSort: (sort: string[][]) => void; opts: { + /** + * Date histogram aggregation config + */ chartAggConfigs?: AggConfigs; + /** + * Client of uiSettings + */ config: IUiSettingsClient; + /** + * Data plugin + */ data: DataPublicPluginStart; - fixedScroll: (el: HTMLElement) => void; + /** + * Data plugin filter manager + */ filterManager: FilterManager; + /** + * List of available index patterns + */ indexPatternList: Array>; + /** + * The number of documents that can be displayed in the table/grid + */ sampleSize: number; + /** + * Current instance of SavedSearch + */ savedSearch: SavedSearch; + /** + * Function to set the header menu + */ setHeaderActionMenu: (menuMount: MountPoint | undefined) => void; + /** + * Timefield of the currently used index pattern + */ timefield: string; + /** + * Function to set the current state + */ setAppState: (state: Partial) => void; }; + /** + * Function to reset the current query + */ resetQuery: () => void; + /** + * Current state of the actual query, one of 'uninitialized', 'loading' ,'ready', 'none' + */ resultState: string; + /** + * Array of document of the recent successful search request + */ rows: ElasticSearchHit[]; + /** + * Instance of SearchSource, the high level search API + */ searchSource: ISearchSource; + /** + * Function to change the current index pattern + */ setIndexPattern: (id: string) => void; + /** + * Determines whether the user should be able to use the save query feature + */ showSaveQuery: boolean; + /** + * Current app state of URL + */ state: AppState; + /** + * Function to update the time filter + */ timefilterUpdateHandler: (ranges: { from: number; to: number }) => void; + /** + * Currently selected time range + */ timeRange?: { from: string; to: string }; + /** + * Menu data of top navigation (New, save ...) + */ topNavMenu: TopNavMenuData[]; + /** + * Function to update the actual query + */ updateQuery: (payload: { dateRange: TimeRange; query?: Query }, isUpdate?: boolean) => void; + /** + * Function to update the actual savedQuery id + */ updateSavedQueryId: (savedQueryId?: string) => void; } @@ -114,7 +229,6 @@ export const SidebarMemoized = React.memo((props: DiscoverSidebarResponsiveProps )); export function DiscoverLegacy({ - addColumn, fetch, fetchCounter, fieldCounts, @@ -123,6 +237,7 @@ export function DiscoverLegacy({ hits, indexPattern, minimumVisibleRows, + onAddColumn, onAddFilter, onChangeInterval, onMoveColumn, @@ -192,7 +307,7 @@ export function DiscoverLegacy({ fieldCounts={fieldCounts} hits={rows} indexPatternList={indexPatternList} - onAddField={addColumn} + onAddField={onAddColumn} onAddFilter={onAddFilter} onRemoveField={onRemoveColumn} selectedIndexPattern={searchSource && searchSource.getField('index')} @@ -206,6 +321,8 @@ export function DiscoverLegacy({ setIsSidebarClosed(!isSidebarClosed)} data-test-subj="collapseSideBarButton" aria-controls="discover-sidebar" @@ -335,7 +452,7 @@ export function DiscoverLegacy({ sort={state.sort || []} searchDescription={opts.savedSearch.description} searchTitle={opts.savedSearch.lastSavedTitle} - onAddColumn={addColumn} + onAddColumn={onAddColumn} onFilter={onAddFilter} onMoveColumn={onMoveColumn} onRemoveColumn={onRemoveColumn} diff --git a/src/plugins/discover/public/application/components/doc_viewer/__snapshots__/doc_viewer.test.tsx.snap b/src/plugins/discover/public/application/components/doc_viewer/__snapshots__/doc_viewer.test.tsx.snap index b5bd961037e21..d02b484a06a49 100644 --- a/src/plugins/discover/public/application/components/doc_viewer/__snapshots__/doc_viewer.test.tsx.snap +++ b/src/plugins/discover/public/application/components/doc_viewer/__snapshots__/doc_viewer.test.tsx.snap @@ -6,6 +6,7 @@ exports[`Render with 3 different tabs 1`] = ` > - +
); } diff --git a/src/plugins/discover/public/application/components/field_name/__snapshots__/field_name.test.tsx.snap b/src/plugins/discover/public/application/components/field_name/__snapshots__/field_name.test.tsx.snap index 2fa96f9372380..6b5e45f8a0448 100644 --- a/src/plugins/discover/public/application/components/field_name/__snapshots__/field_name.test.tsx.snap +++ b/src/plugins/discover/public/application/components/field_name/__snapshots__/field_name.test.tsx.snap @@ -31,7 +31,7 @@ exports[`FieldName renders a geo field 1`] = `
`; -exports[`FieldName renders a number field by providing a field record, useShortDots is set to false 1`] = ` +exports[`FieldName renders a number field by providing a field record 1`] = `
diff --git a/src/plugins/discover/public/application/components/field_name/field_name.test.tsx b/src/plugins/discover/public/application/components/field_name/field_name.test.tsx index 0deddce1c40a8..248191acf9ab9 100644 --- a/src/plugins/discover/public/application/components/field_name/field_name.test.tsx +++ b/src/plugins/discover/public/application/components/field_name/field_name.test.tsx @@ -27,7 +27,7 @@ test('FieldName renders a string field by providing fieldType and fieldName', () expect(component).toMatchSnapshot(); }); -test('FieldName renders a number field by providing a field record, useShortDots is set to false', () => { +test('FieldName renders a number field by providing a field record', () => { const component = render(); expect(component).toMatchSnapshot(); }); diff --git a/src/plugins/discover/public/application/components/sidebar/discover_field.test.tsx b/src/plugins/discover/public/application/components/sidebar/discover_field.test.tsx index 391e15485f074..0957ee101bd27 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_field.test.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_field.test.tsx @@ -56,7 +56,6 @@ function getComponent({ }: { selected?: boolean; showDetails?: boolean; - useShortDots?: boolean; field?: IndexPatternField; }) { const indexPattern = getStubIndexPattern( diff --git a/src/plugins/discover/public/application/components/sidebar/lib/group_fields.test.ts b/src/plugins/discover/public/application/components/sidebar/lib/group_fields.test.ts index d4670a1e76011..22cacae4c3b45 100644 --- a/src/plugins/discover/public/application/components/sidebar/lib/group_fields.test.ts +++ b/src/plugins/discover/public/application/components/sidebar/lib/group_fields.test.ts @@ -19,51 +19,58 @@ import { groupFields } from './group_fields'; import { getDefaultFieldFilter } from './field_filter'; +import { IndexPatternField } from '../../../../../../data/common/index_patterns/fields'; -describe('group_fields', function () { - it('should group fields in selected, popular, unpopular group', function () { - const fields = [ - { - name: 'category', - type: 'string', - esTypes: ['text'], - count: 1, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - { - name: 'currency', - type: 'string', - esTypes: ['keyword'], - count: 0, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - { - name: 'customer_birth_date', - type: 'date', - esTypes: ['date'], - count: 0, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - ]; +const fields = [ + { + name: 'category', + type: 'string', + esTypes: ['text'], + count: 1, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'currency', + type: 'string', + esTypes: ['keyword'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'customer_birth_date', + type: 'date', + esTypes: ['date'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, +]; - const fieldCounts = { - category: 1, - currency: 1, - customer_birth_date: 1, - }; +const fieldCounts = { + category: 1, + currency: 1, + customer_birth_date: 1, +}; +describe('group_fields', function () { + it('should group fields in selected, popular, unpopular group', function () { const fieldFilterState = getDefaultFieldFilter(); - const actual = groupFields(fields as any, ['currency'], 5, fieldCounts, fieldFilterState); + const actual = groupFields( + fields as IndexPatternField[], + ['currency'], + 5, + fieldCounts, + fieldFilterState + ); expect(actual).toMatchInlineSnapshot(` Object { "popular": Array [ @@ -111,4 +118,34 @@ describe('group_fields', function () { } `); }); + + it('should sort selected fields by columns order ', function () { + const fieldFilterState = getDefaultFieldFilter(); + + const actual1 = groupFields( + fields as IndexPatternField[], + ['customer_birth_date', 'currency', 'unknown'], + 5, + fieldCounts, + fieldFilterState + ); + expect(actual1.selected.map((field) => field.name)).toEqual([ + 'customer_birth_date', + 'currency', + 'unknown', + ]); + + const actual2 = groupFields( + fields as IndexPatternField[], + ['currency', 'customer_birth_date', 'unknown'], + 5, + fieldCounts, + fieldFilterState + ); + expect(actual2.selected.map((field) => field.name)).toEqual([ + 'currency', + 'customer_birth_date', + 'unknown', + ]); + }); }); diff --git a/src/plugins/discover/public/application/components/sidebar/lib/group_fields.tsx b/src/plugins/discover/public/application/components/sidebar/lib/group_fields.tsx index c6a06618900fd..c34becc97cb93 100644 --- a/src/plugins/discover/public/application/components/sidebar/lib/group_fields.tsx +++ b/src/plugins/discover/public/application/components/sidebar/lib/group_fields.tsx @@ -70,6 +70,15 @@ export function groupFields( result.unpopular.push(field); } } + // add columns, that are not part of the index pattern, to be removeable + for (const column of columns) { + if (!result.selected.find((field) => field.name === column)) { + result.selected.push({ name: column, displayName: column } as IndexPatternField); + } + } + result.selected.sort((a, b) => { + return columns.indexOf(a.name) - columns.indexOf(b.name); + }); return result; } diff --git a/src/plugins/discover/public/application/embeddable/search_embeddable.ts b/src/plugins/discover/public/application/embeddable/search_embeddable.ts index d0c3907d31242..e4a8ab7bc67ff 100644 --- a/src/plugins/discover/public/application/embeddable/search_embeddable.ts +++ b/src/plugins/discover/public/application/embeddable/search_embeddable.ts @@ -36,6 +36,7 @@ import { import { Container, Embeddable } from '../../../../embeddable/public'; import * as columnActions from '../angular/doc_table/actions/columns'; import searchTemplate from './search_template.html'; +import searchTemplateGrid from './search_template_datagrid.html'; import { ISearchEmbeddable, SearchInput, SearchOutput } from './types'; import { SortOrder } from '../angular/doc_table/components/table_header/helpers'; import { getSortForSearchSource } from '../angular/doc_table'; @@ -49,23 +50,29 @@ import { import { SEARCH_EMBEDDABLE_TYPE } from './constants'; import { SavedSearch } from '../..'; import { SAMPLE_SIZE_SETTING, SORT_DEFAULT_ORDER_SETTING } from '../../../common'; +import { DiscoverGridSettings } from '../components/discover_grid/types'; +import { DiscoverServices } from '../../build_services'; +import { ElasticSearchHit } from '../doc_views/doc_views_types'; import { getDefaultSort } from '../angular/doc_table/lib/get_default_sort'; interface SearchScope extends ng.IScope { columns?: string[]; + settings?: DiscoverGridSettings; description?: string; sort?: SortOrder[]; sharedItemTitle?: string; inspectorAdapters?: Adapters; setSortOrder?: (sortPair: SortOrder[]) => void; + setColumns?: (columns: string[]) => void; removeColumn?: (column: string) => void; addColumn?: (column: string) => void; moveColumn?: (column: string, index: number) => void; filter?: (field: IFieldType, value: string[], operator: string) => void; - hits?: any[]; + hits?: ElasticSearchHit[]; indexPattern?: IndexPattern; totalHitCount?: number; isLoading?: boolean; + showTimeCol?: boolean; } interface SearchEmbeddableConfig { @@ -77,6 +84,7 @@ interface SearchEmbeddableConfig { indexPatterns?: IndexPattern[]; editable: boolean; filterManager: FilterManager; + services: DiscoverServices; } export class SearchEmbeddable @@ -95,6 +103,7 @@ export class SearchEmbeddable public readonly type = SEARCH_EMBEDDABLE_TYPE; private filterManager: FilterManager; private abortController?: AbortController; + private services: DiscoverServices; private prevTimeRange?: TimeRange; private prevFilters?: Filter[]; @@ -111,6 +120,7 @@ export class SearchEmbeddable indexPatterns, editable, filterManager, + services, }: SearchEmbeddableConfig, initialInput: SearchInput, private readonly executeTriggerActions: UiActionsStart['executeTriggerActions'], @@ -128,7 +138,7 @@ export class SearchEmbeddable }, parent ); - + this.services = services; this.filterManager = filterManager; this.savedSearch = savedSearch; this.$rootScope = $rootScope; @@ -138,8 +148,8 @@ export class SearchEmbeddable }; this.initializeSearchScope(); - this.autoRefreshFetchSubscription = getServices() - .timefilter.getAutoRefreshFetch$() + this.autoRefreshFetchSubscription = this.services.timefilter + .getAutoRefreshFetch$() .subscribe(this.fetch); this.subscription = this.getUpdated$().subscribe(() => { @@ -167,7 +177,9 @@ export class SearchEmbeddable if (!this.searchScope) { throw new Error('Search scope not defined'); } - this.searchInstance = this.$compile(searchTemplate)(this.searchScope); + this.searchInstance = this.$compile( + this.services.uiSettings.get('doc_table:legacy', true) ? searchTemplate : searchTemplateGrid + )(this.searchScope); const rootNode = angular.element(domNode); rootNode.append(this.searchInstance); @@ -250,6 +262,15 @@ export class SearchEmbeddable this.updateInput({ columns }); }; + searchScope.setColumns = (columns: string[]) => { + this.updateInput({ columns }); + }; + + if (this.savedSearch.grid) { + searchScope.settings = this.savedSearch.grid; + } + searchScope.showTimeCol = !this.services.uiSettings.get('doc_table:hideTimeColumn', false); + searchScope.filter = async (field, value, operator) => { let filters = esFilters.generateFilters( this.filterManager, @@ -286,13 +307,13 @@ export class SearchEmbeddable if (this.abortController) this.abortController.abort(); this.abortController = new AbortController(); - searchSource.setField('size', getServices().uiSettings.get(SAMPLE_SIZE_SETTING)); + searchSource.setField('size', this.services.uiSettings.get(SAMPLE_SIZE_SETTING)); searchSource.setField( 'sort', getSortForSearchSource( this.searchScope.sort, this.searchScope.indexPattern, - getServices().uiSettings.get(SORT_DEFAULT_ORDER_SETTING) + this.services.uiSettings.get(SORT_DEFAULT_ORDER_SETTING) ) ); diff --git a/src/plugins/discover/public/application/embeddable/search_embeddable_factory.ts b/src/plugins/discover/public/application/embeddable/search_embeddable_factory.ts index f61fa361f0c0e..d85476568201f 100644 --- a/src/plugins/discover/public/application/embeddable/search_embeddable_factory.ts +++ b/src/plugins/discover/public/application/embeddable/search_embeddable_factory.ts @@ -103,6 +103,7 @@ export class SearchEmbeddableFactory filterManager, editable: getServices().capabilities.discover.save as boolean, indexPatterns: indexPattern ? [indexPattern] : [], + services: getServices(), }, input, executeTriggerActions, diff --git a/src/plugins/discover/public/application/embeddable/search_template.html b/src/plugins/discover/public/application/embeddable/search_template.html index e188d230ea307..be2f5cceac080 100644 --- a/src/plugins/discover/public/application/embeddable/search_template.html +++ b/src/plugins/discover/public/application/embeddable/search_template.html @@ -1,20 +1,20 @@ diff --git a/src/plugins/discover/public/application/embeddable/search_template_datagrid.html b/src/plugins/discover/public/application/embeddable/search_template_datagrid.html new file mode 100644 index 0000000000000..6524783897f8f --- /dev/null +++ b/src/plugins/discover/public/application/embeddable/search_template_datagrid.html @@ -0,0 +1,19 @@ + diff --git a/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts b/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts index 4dec1f75ba322..2ab1b93d6c37e 100644 --- a/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts +++ b/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts @@ -51,7 +51,7 @@ describe('getSharingData', () => { "searchRequest": Object { "body": Object { "_source": Object {}, - "fields": undefined, + "fields": Array [], "query": Object { "bool": Object { "filter": Array [], @@ -68,7 +68,9 @@ describe('getSharingData', () => { }, }, ], - "stored_fields": undefined, + "stored_fields": Array [ + "*", + ], }, "index": "the-index-pattern-title", }, diff --git a/src/plugins/discover/public/application/helpers/persist_saved_search.ts b/src/plugins/discover/public/application/helpers/persist_saved_search.ts index 8e956eff598f3..8ec2012b5843e 100644 --- a/src/plugins/discover/public/application/helpers/persist_saved_search.ts +++ b/src/plugins/discover/public/application/helpers/persist_saved_search.ts @@ -53,6 +53,9 @@ export async function persistSavedSearch( savedSearch.columns = state.columns || []; savedSearch.sort = (state.sort as SortOrder[]) || []; + if (state.grid) { + savedSearch.grid = state.grid; + } try { const id = await savedSearch.save(saveOptions); diff --git a/src/plugins/discover/public/get_inner_angular.ts b/src/plugins/discover/public/get_inner_angular.ts index 651a26cad755d..c32cf3023a25e 100644 --- a/src/plugins/discover/public/get_inner_angular.ts +++ b/src/plugins/discover/public/get_inner_angular.ts @@ -41,6 +41,7 @@ import { createTableRowDirective } from './application/angular/doc_table/compone import { createPagerFactory } from './application/angular/doc_table/lib/pager/pager_factory'; import { createInfiniteScrollDirective } from './application/angular/doc_table/infinite_scroll'; import { createDocViewerDirective } from './application/angular/doc_viewer'; +import { createDiscoverGridDirective } from './application/components/create_discover_grid_directive'; import { createRenderCompleteDirective } from './application/angular/directives/render_complete'; import { initAngularBootstrap, @@ -49,12 +50,12 @@ import { PromiseServiceCreator, registerListenEventListener, watchMultiDecorator, - createTopNavDirective, - createTopNavHelper, } from '../../kibana_legacy/public'; import { DiscoverStartPlugins } from './plugin'; import { getScopedHistory } from './kibana_services'; import { createDiscoverLegacyDirective } from './application/components/create_discover_legacy_directive'; +import { createDiscoverDirective } from './application/components/create_discover_directive'; + /** * returns the main inner angular module, it contains all the parts of Angular Discover * needs to render, so in the end the current 'kibana' angular module is no longer necessary @@ -95,7 +96,6 @@ export function initializeInnerAngularModule( createLocalI18nModule(); createLocalPrivateModule(); createLocalPromiseModule(); - createLocalTopNavModule(navigation); createLocalStorageModule(); createPagerFactoryModule(); createDocTableModule(); @@ -128,7 +128,6 @@ export function initializeInnerAngularModule( 'discoverI18n', 'discoverPrivate', 'discoverPromise', - 'discoverTopNav', 'discoverLocalStorageProvider', 'discoverDocTable', 'discoverPagerFactory', @@ -136,7 +135,8 @@ export function initializeInnerAngularModule( .config(watchMultiDecorator) .run(registerListenEventListener) .directive('renderComplete', createRenderCompleteDirective) - .directive('discoverLegacy', createDiscoverLegacyDirective); + .directive('discoverLegacy', createDiscoverLegacyDirective) + .directive('discover', createDiscoverDirective); } function createLocalPromiseModule() { @@ -147,13 +147,6 @@ function createLocalPrivateModule() { angular.module('discoverPrivate', []).provider('Private', PrivateProvider); } -function createLocalTopNavModule(navigation: NavigationStart) { - angular - .module('discoverTopNav', ['react']) - .directive('kbnTopNav', createTopNavDirective) - .directive('kbnTopNavHelper', createTopNavHelper(navigation.ui)); -} - function createLocalI18nModule() { angular .module('discoverI18n', []) @@ -188,6 +181,7 @@ function createDocTableModule() { .directive('kbnTableRow', createTableRowDirective) .directive('toolBarPagerButtons', createToolBarPagerButtonsDirective) .directive('kbnInfiniteScroll', createInfiniteScrollDirective) + .directive('discoverGrid', createDiscoverGridDirective) .directive('docViewer', createDocViewerDirective) .directive('contextAppLegacy', createContextAppLegacy); } diff --git a/src/plugins/discover/public/saved_searches/_saved_search.ts b/src/plugins/discover/public/saved_searches/_saved_search.ts index 1ec4549f05d49..8a0ec128b4eb2 100644 --- a/src/plugins/discover/public/saved_searches/_saved_search.ts +++ b/src/plugins/discover/public/saved_searches/_saved_search.ts @@ -26,6 +26,7 @@ export function createSavedSearchClass(savedObjects: SavedObjectsStart) { description: 'text', hits: 'integer', columns: 'keyword', + grid: 'object', sort: 'keyword', version: 'integer', }; @@ -45,6 +46,7 @@ export function createSavedSearchClass(savedObjects: SavedObjectsStart) { description: 'text', hits: 'integer', columns: 'keyword', + grid: 'object', sort: 'keyword', version: 'integer', }, diff --git a/src/plugins/discover/public/saved_searches/types.ts b/src/plugins/discover/public/saved_searches/types.ts index d5e5dd765a364..7f6f1a2553d5e 100644 --- a/src/plugins/discover/public/saved_searches/types.ts +++ b/src/plugins/discover/public/saved_searches/types.ts @@ -19,6 +19,7 @@ import { SearchSource } from '../../../data/public'; import { SavedObjectSaveOpts } from '../../../saved_objects/public'; +import { DiscoverGridSettings } from '../application/components/discover_grid/types'; export type SortOrder = [string, string]; export interface SavedSearch { @@ -28,6 +29,7 @@ export interface SavedSearch { description?: string; columns: string[]; sort: SortOrder[]; + grid: DiscoverGridSettings; destroy: () => void; save: (saveOptions: SavedObjectSaveOpts) => Promise; lastSavedTitle?: string; diff --git a/src/plugins/discover/server/saved_objects/search.ts b/src/plugins/discover/server/saved_objects/search.ts index a6e42f956a025..d124a24b120fd 100644 --- a/src/plugins/discover/server/saved_objects/search.ts +++ b/src/plugins/discover/server/saved_objects/search.ts @@ -53,6 +53,7 @@ export const searchSavedObjectType: SavedObjectsType = { }, sort: { type: 'keyword', index: false, doc_values: false }, title: { type: 'text' }, + grid: { type: 'object', enabled: false }, version: { type: 'integer' }, }, }, diff --git a/src/plugins/discover/server/ui_settings.ts b/src/plugins/discover/server/ui_settings.ts index f45281ee62202..425928385e64a 100644 --- a/src/plugins/discover/server/ui_settings.ts +++ b/src/plugins/discover/server/ui_settings.ts @@ -33,6 +33,7 @@ import { CONTEXT_DEFAULT_SIZE_SETTING, CONTEXT_STEP_SETTING, CONTEXT_TIE_BREAKER_FIELDS_SETTING, + DOC_TABLE_LEGACY, MODIFY_COLUMNS_ON_SWITCH, } from '../common'; @@ -165,6 +166,23 @@ export const uiSettings: Record = { category: ['discover'], schema: schema.arrayOf(schema.string()), }, + [DOC_TABLE_LEGACY]: { + name: i18n.translate('discover.advancedSettings.docTableVersionName', { + defaultMessage: 'Use legacy table', + }), + value: true, + description: i18n.translate('discover.advancedSettings.docTableVersionDescription', { + defaultMessage: + 'Discover uses a new table layout that includes better data sorting, drag-and-drop columns, and a full screen ' + + 'view. Enable this option if you prefer to fall back to the legacy table.', + }), + category: ['discover'], + schema: schema.boolean(), + metric: { + type: METRIC_TYPE.CLICK, + name: 'discover:useLegacyDataGrid', + }, + }, [MODIFY_COLUMNS_ON_SWITCH]: { name: i18n.translate('discover.advancedSettings.discover.modifyColumnsOnSwitchTitle', { defaultMessage: 'Modify columns when changing index patterns', diff --git a/src/plugins/embeddable/common/types.ts b/src/plugins/embeddable/common/types.ts index d893724f616d2..8366d81a65754 100644 --- a/src/plugins/embeddable/common/types.ts +++ b/src/plugins/embeddable/common/types.ts @@ -55,6 +55,11 @@ export type EmbeddableInput = { * Search session id to group searches */ searchSessionId?: string; + + /** + * Flag whether colors should be synced with other panels + */ + syncColors?: boolean; }; export interface PanelState { diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/open_add_panel_flyout.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/open_add_panel_flyout.tsx index 867092b78ef7a..3363f556b418e 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/open_add_panel_flyout.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/open_add_panel_flyout.tsx @@ -17,20 +17,20 @@ * under the License. */ import React from 'react'; -import { NotificationsStart, OverlayStart } from 'src/core/public'; +import { NotificationsStart, OverlayRef, OverlayStart } from 'src/core/public'; import { EmbeddableStart } from '../../../../../plugin'; import { toMountPoint } from '../../../../../../../kibana_react/public'; import { IContainer } from '../../../../containers'; import { AddPanelFlyout } from './add_panel_flyout'; -export async function openAddPanelFlyout(options: { +export function openAddPanelFlyout(options: { embeddable: IContainer; getFactory: EmbeddableStart['getEmbeddableFactory']; getAllFactories: EmbeddableStart['getEmbeddableFactories']; overlays: OverlayStart; notifications: NotificationsStart; SavedObjectFinder: React.ComponentType; -}) { +}): OverlayRef { const { embeddable, getFactory, @@ -59,4 +59,5 @@ export async function openAddPanelFlyout(options: { ownFocus: true, } ); + return flyoutSession; } diff --git a/src/plugins/embeddable/public/public.api.md b/src/plugins/embeddable/public/public.api.md index a401795c498b3..b20d5866298d5 100644 --- a/src/plugins/embeddable/public/public.api.md +++ b/src/plugins/embeddable/public/public.api.md @@ -34,6 +34,7 @@ import { MaybePromise } from '@kbn/utility-types'; import { NotificationsStart as NotificationsStart_2 } from 'src/core/public'; import { Observable } from 'rxjs'; import { Optional } from '@kbn/utility-types'; +import { OverlayRef as OverlayRef_2 } from 'src/core/public'; import { OverlayStart as OverlayStart_2 } from 'src/core/public'; import { PackageInfo } from '@kbn/config'; import { Path } from 'history'; @@ -410,6 +411,7 @@ export type EmbeddableInput = { disabledActions?: string[]; disableTriggers?: boolean; searchSessionId?: string; + syncColors?: boolean; }; // Warning: (ae-missing-release-tag) "EmbeddableInstanceConfiguration" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -716,7 +718,7 @@ export function openAddPanelFlyout(options: { overlays: OverlayStart_2; notifications: NotificationsStart_2; SavedObjectFinder: React.ComponentType; -}): Promise; +}): OverlayRef_2; // Warning: (ae-missing-release-tag) "OutputSpec" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // diff --git a/src/plugins/expressions/common/expression_renderers/types.ts b/src/plugins/expressions/common/expression_renderers/types.ts index fca1694747ce2..3f3cfb9ed2dd9 100644 --- a/src/plugins/expressions/common/expression_renderers/types.ts +++ b/src/plugins/expressions/common/expression_renderers/types.ts @@ -82,6 +82,7 @@ export interface IInterpreterRenderHandlers { event: (event: any) => void; hasCompatibleActions?: (event: any) => Promise; getRenderMode: () => RenderMode; + isSyncColorsEnabled: () => boolean; /** * This uiState interface is actually `PersistedState` from the visualizations plugin, * but expressions cannot know about vis or it creates a mess of circular dependencies. diff --git a/src/plugins/expressions/public/loader.ts b/src/plugins/expressions/public/loader.ts index e9e0fa18af6c3..1cf499ce2635a 100644 --- a/src/plugins/expressions/public/loader.ts +++ b/src/plugins/expressions/public/loader.ts @@ -64,6 +64,7 @@ export class ExpressionLoader { this.renderHandler = new ExpressionRenderHandler(element, { onRenderError: params && params.onRenderError, renderMode: params?.renderMode, + syncColors: params?.syncColors, hasCompatibleActions: params?.hasCompatibleActions, }); this.render$ = this.renderHandler.render$; diff --git a/src/plugins/expressions/public/public.api.md b/src/plugins/expressions/public/public.api.md index 404df2db019a1..5c018adc0131b 100644 --- a/src/plugins/expressions/public/public.api.md +++ b/src/plugins/expressions/public/public.api.md @@ -531,7 +531,7 @@ export interface ExpressionRenderError extends Error { // @public (undocumented) export class ExpressionRenderHandler { // Warning: (ae-forgotten-export) The symbol "ExpressionRenderHandlerParams" needs to be exported by the entry point index.d.ts - constructor(element: HTMLElement, { onRenderError, renderMode, hasCompatibleActions, }?: ExpressionRenderHandlerParams); + constructor(element: HTMLElement, { onRenderError, renderMode, syncColors, hasCompatibleActions, }?: ExpressionRenderHandlerParams); // (undocumented) destroy: () => void; // (undocumented) @@ -903,6 +903,8 @@ export interface IExpressionLoaderParams { // (undocumented) searchSessionId?: string; // (undocumented) + syncColors?: boolean; + // (undocumented) uiState?: unknown; // (undocumented) variables?: Record; @@ -920,6 +922,8 @@ export interface IInterpreterRenderHandlers { // (undocumented) hasCompatibleActions?: (event: any) => Promise; // (undocumented) + isSyncColorsEnabled: () => boolean; + // (undocumented) onDestroy: (fn: () => void) => void; // (undocumented) reload: () => void; diff --git a/src/plugins/expressions/public/react_expression_renderer.test.tsx b/src/plugins/expressions/public/react_expression_renderer.test.tsx index 4ebd626e70fc3..d9a4f095127b8 100644 --- a/src/plugins/expressions/public/react_expression_renderer.test.tsx +++ b/src/plugins/expressions/public/react_expression_renderer.test.tsx @@ -146,6 +146,35 @@ describe('ExpressionRenderer', () => { instance.unmount(); }); + it('should not update twice immediately after rendering', () => { + jest.useFakeTimers(); + + const refreshSubject = new Subject(); + const loaderUpdate = jest.fn(); + + (ExpressionLoader as jest.Mock).mockImplementation(() => { + return { + render$: new Subject(), + data$: new Subject(), + loading$: new Subject(), + update: loaderUpdate, + destroy: jest.fn(), + }; + }); + + const instance = mount( + + ); + + act(() => { + jest.runAllTimers(); + }); + + expect(loaderUpdate).toHaveBeenCalledTimes(1); + + instance.unmount(); + }); + it('waits for debounce period on other loader option change if specified', () => { jest.useFakeTimers(); @@ -304,4 +333,22 @@ describe('ExpressionRenderer', () => { expect(onEvent).toHaveBeenCalledTimes(1); expect(onEvent.mock.calls[0][0]).toBe(event); }); + + it('should correctly assign classes to the wrapper node', () => { + (ExpressionLoader as jest.Mock).mockImplementation(() => { + return { + render$: new Subject(), + data$: new Subject(), + loading$: new Subject(), + update: jest.fn(), + destroy: jest.fn(), + }; + }); + + const instance = mount(); + // Counte is 2 because the class is applied to ReactExpressionRenderer + internal component + expect(instance.find('.myClassName').length).toBe(2); + + instance.unmount(); + }); }); diff --git a/src/plugins/expressions/public/react_expression_renderer.tsx b/src/plugins/expressions/public/react_expression_renderer.tsx index eac2371ec66d0..caa8e209ec170 100644 --- a/src/plugins/expressions/public/react_expression_renderer.tsx +++ b/src/plugins/expressions/public/react_expression_renderer.tsx @@ -91,7 +91,12 @@ export const ReactExpressionRenderer = ({ ); const [debouncedExpression, setDebouncedExpression] = useState(expression); const [waitingForDebounceToComplete, setDebouncePending] = useState(false); + const firstRender = useRef(true); useShallowCompareEffect(() => { + if (firstRender.current) { + firstRender.current = false; + return; + } if (debounce === undefined) { return; } @@ -170,7 +175,12 @@ export const ReactExpressionRenderer = ({ errorRenderHandlerRef.current = null; }; - }, [hasCustomRenderErrorHandler, onEvent]); + }, [ + hasCustomRenderErrorHandler, + onEvent, + expressionLoaderOptions.renderMode, + expressionLoaderOptions.syncColors, + ]); useEffect(() => { const subscription = reload$?.subscribe(() => { @@ -206,10 +216,9 @@ export const ReactExpressionRenderer = ({ } }, [state.error]); - const classes = classNames('expExpressionRenderer', { + const classes = classNames('expExpressionRenderer', className, { 'expExpressionRenderer-isEmpty': state.isEmpty, 'expExpressionRenderer-hasError': !!state.error, - className, }); const expressionStyles: React.CSSProperties = {}; diff --git a/src/plugins/expressions/public/render.ts b/src/plugins/expressions/public/render.ts index 717776a2861b4..e3091b908deca 100644 --- a/src/plugins/expressions/public/render.ts +++ b/src/plugins/expressions/public/render.ts @@ -31,6 +31,7 @@ export type IExpressionRendererExtraHandlers = Record; export interface ExpressionRenderHandlerParams { onRenderError?: RenderErrorHandlerFnType; renderMode?: RenderMode; + syncColors?: boolean; hasCompatibleActions?: (event: ExpressionRendererEvent) => Promise; } @@ -63,6 +64,7 @@ export class ExpressionRenderHandler { { onRenderError, renderMode, + syncColors, hasCompatibleActions = async () => false, }: ExpressionRenderHandlerParams = {} ) { @@ -101,6 +103,9 @@ export class ExpressionRenderHandler { getRenderMode: () => { return renderMode || 'display'; }, + isSyncColorsEnabled: () => { + return syncColors || false; + }, hasCompatibleActions, }; } diff --git a/src/plugins/expressions/public/types/index.ts b/src/plugins/expressions/public/types/index.ts index f37107abbb716..d709d8ca96bbd 100644 --- a/src/plugins/expressions/public/types/index.ts +++ b/src/plugins/expressions/public/types/index.ts @@ -57,6 +57,7 @@ export interface IExpressionLoaderParams { onRenderError?: RenderErrorHandlerFnType; searchSessionId?: string; renderMode?: RenderMode; + syncColors?: boolean; hasCompatibleActions?: ExpressionRenderHandlerParams['hasCompatibleActions']; } diff --git a/src/plugins/expressions/server/server.api.md b/src/plugins/expressions/server/server.api.md index 8b8678371dd83..71199560ee0c7 100644 --- a/src/plugins/expressions/server/server.api.md +++ b/src/plugins/expressions/server/server.api.md @@ -737,6 +737,8 @@ export interface IInterpreterRenderHandlers { // (undocumented) hasCompatibleActions?: (event: any) => Promise; // (undocumented) + isSyncColorsEnabled: () => boolean; + // (undocumented) onDestroy: (fn: () => void) => void; // (undocumented) reload: () => void; diff --git a/src/plugins/home/server/services/sample_data/usage/collector_fetch.test.ts b/src/plugins/home/server/services/sample_data/usage/collector_fetch.test.ts index 54fed3db1de4d..58bb037f8d614 100644 --- a/src/plugins/home/server/services/sample_data/usage/collector_fetch.test.ts +++ b/src/plugins/home/server/services/sample_data/usage/collector_fetch.test.ts @@ -23,7 +23,7 @@ import { fetchProvider } from './collector_fetch'; const getMockFetchClients = (hits?: unknown[]) => { const fetchParamsMock = createCollectorFetchContextMock(); - fetchParamsMock.callCluster.mockResolvedValue({ hits: { hits } }); + fetchParamsMock.esClient.search = jest.fn().mockResolvedValue({ body: { hits: { hits } } }); return fetchParamsMock; }; diff --git a/src/plugins/home/server/services/sample_data/usage/collector_fetch.ts b/src/plugins/home/server/services/sample_data/usage/collector_fetch.ts index 7df9b14d2efb1..ef958873d9663 100644 --- a/src/plugins/home/server/services/sample_data/usage/collector_fetch.ts +++ b/src/plugins/home/server/services/sample_data/usage/collector_fetch.ts @@ -19,6 +19,7 @@ import { get } from 'lodash'; import moment from 'moment'; +import { SearchResponse } from 'src/core/server'; import { CollectorFetchContext } from '../../../../../usage_collection/server'; interface SearchHit { @@ -41,17 +42,23 @@ export interface TelemetryResponse { last_uninstall_set: string | null; } +type ESResponse = SearchResponse; + export function fetchProvider(index: string) { - return async ({ callCluster }: CollectorFetchContext) => { - const response = await callCluster('search', { - index, - body: { - query: { term: { type: { value: 'sample-data-telemetry' } } }, - _source: { includes: ['sample-data-telemetry', 'type', 'updated_at'] }, + return async ({ esClient }: CollectorFetchContext) => { + const { body: response } = await esClient.search( + { + index, + body: { + query: { term: { type: { value: 'sample-data-telemetry' } } }, + _source: { includes: ['sample-data-telemetry', 'type', 'updated_at'] }, + }, + filter_path: 'hits.hits._id,hits.hits._source', }, - filter_path: 'hits.hits._id,hits.hits._source', - ignore: [404], - }); + { + ignore: [404], + } + ); const getLast = ( dataSet: string, diff --git a/src/plugins/maps_legacy/kibana.json b/src/plugins/maps_legacy/kibana.json index 1499b3de446b5..9d4586ebce53b 100644 --- a/src/plugins/maps_legacy/kibana.json +++ b/src/plugins/maps_legacy/kibana.json @@ -6,5 +6,5 @@ "ui": true, "server": true, "extraPublicDirs": ["common"], - "requiredBundles": ["kibanaReact", "charts"] + "requiredBundles": ["kibanaReact", "visDefaultEditor"] } diff --git a/src/plugins/maps_legacy/public/components/wms_internal_options.tsx b/src/plugins/maps_legacy/public/components/wms_internal_options.tsx index d1def8153d1a8..86c15f10ae55d 100644 --- a/src/plugins/maps_legacy/public/components/wms_internal_options.tsx +++ b/src/plugins/maps_legacy/public/components/wms_internal_options.tsx @@ -21,7 +21,7 @@ import React from 'react'; import { EuiLink, EuiSpacer, EuiText, EuiScreenReaderOnly } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { TextInputOption } from '../../../charts/public'; +import { TextInputOption } from '../../../vis_default_editor/public'; import { WMSOptions } from '../common/types/external_basemap_types'; interface WmsInternalOptions { diff --git a/src/plugins/maps_legacy/public/components/wms_options.tsx b/src/plugins/maps_legacy/public/components/wms_options.tsx index 4892463bb9f85..79e08478f2155 100644 --- a/src/plugins/maps_legacy/public/components/wms_options.tsx +++ b/src/plugins/maps_legacy/public/components/wms_options.tsx @@ -24,7 +24,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { TmsLayer } from '../index'; import { Vis } from '../../../visualizations/public'; import { RegionMapVisParams } from '../common/types/region_map_types'; -import { SelectOption, SwitchOption } from '../../../charts/public'; +import { SelectOption, SwitchOption } from '../../../vis_default_editor/public'; import { WmsInternalOptions } from './wms_internal_options'; import { WMSOptions, TileMapVisParams } from '../common/types/external_basemap_types'; diff --git a/src/plugins/navigation/public/index.ts b/src/plugins/navigation/public/index.ts index 5afc91c4445e8..a1b72eac756d3 100644 --- a/src/plugins/navigation/public/index.ts +++ b/src/plugins/navigation/public/index.ts @@ -24,7 +24,7 @@ export function plugin(initializerContext: PluginInitializerContext) { return new NavigationPublicPlugin(initializerContext); } -export { TopNavMenuData, TopNavMenu } from './top_nav_menu'; +export { TopNavMenuData, TopNavMenu, TopNavMenuProps } from './top_nav_menu'; export { NavigationPublicPluginSetup, NavigationPublicPluginStart } from './types'; diff --git a/src/plugins/region_map/public/components/region_map_options.tsx b/src/plugins/region_map/public/components/region_map_options.tsx index 4d564d7347a1e..b2bb250d66ee2 100644 --- a/src/plugins/region_map/public/components/region_map_options.tsx +++ b/src/plugins/region_map/public/components/region_map_options.tsx @@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; import { FileLayerField, VectorLayer, IServiceSettings } from '../../../maps_legacy/public'; -import { NumberInputOption, SelectOption, SwitchOption } from '../../../charts/public'; +import { SelectOption, SwitchOption, NumberInputOption } from '../../../vis_default_editor/public'; import { RegionMapVisParams, WmsOptions } from '../../../maps_legacy/public'; const mapLayerForOption = ({ layerId, name }: VectorLayer) => ({ diff --git a/src/plugins/tile_map/public/components/tile_map_options.tsx b/src/plugins/tile_map/public/components/tile_map_options.tsx index 1a7b11ccf6e20..a6c0bb8a50dda 100644 --- a/src/plugins/tile_map/public/components/tile_map_options.tsx +++ b/src/plugins/tile_map/public/components/tile_map_options.tsx @@ -21,8 +21,13 @@ import React, { useEffect } from 'react'; import { EuiPanel, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; -import { BasicOptions, RangeOption, SelectOption, SwitchOption } from '../../../charts/public'; +import { + VisOptionsProps, + BasicOptions, + SelectOption, + SwitchOption, + RangeOption, +} from '../../../vis_default_editor/public'; import { WmsOptions, TileMapVisParams, MapTypes } from '../../../maps_legacy/public'; export type TileMapOptionsProps = VisOptionsProps; diff --git a/src/plugins/vis_default_editor/kibana.json b/src/plugins/vis_default_editor/kibana.json index 35ad0a3a8be9a..9664b14821c0d 100644 --- a/src/plugins/vis_default_editor/kibana.json +++ b/src/plugins/vis_default_editor/kibana.json @@ -2,5 +2,6 @@ "id": "visDefaultEditor", "version": "kibana", "ui": true, + "optionalPlugins": ["visualize"], "requiredBundles": ["kibanaUtils", "kibanaReact", "data"] } diff --git a/src/plugins/charts/public/static/components/basic_options.tsx b/src/plugins/vis_default_editor/public/components/options/basic_options.tsx similarity index 86% rename from src/plugins/charts/public/static/components/basic_options.tsx rename to src/plugins/vis_default_editor/public/components/options/basic_options.tsx index 9c5a22543df99..f67a9997cb5e2 100644 --- a/src/plugins/charts/public/static/components/basic_options.tsx +++ b/src/plugins/vis_default_editor/public/components/options/basic_options.tsx @@ -21,8 +21,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { VisOptionsProps } from '../../../../vis_default_editor/public'; - +import { VisOptionsProps } from '../../vis_options_props'; import { SwitchOption } from './switch'; import { SelectOption } from './select'; @@ -39,7 +38,7 @@ function BasicOptions({ return ( <> ({ setValue={setValue} /> void; @@ -71,7 +71,7 @@ function ColorRanges({ return ( ( paramName: T, @@ -67,7 +66,7 @@ function ColorSchemaOptions({ }} > @@ -80,11 +79,11 @@ function ColorSchemaOptions({ disabled={disabled} helpText={ showHelpText && - i18n.translate('charts.controls.colorSchema.howToChangeColorsDescription', { + i18n.translate('visDefaultEditor.options.colorSchema.howToChangeColorsDescription', { defaultMessage: 'Individual colors can be changed in the legend.', }) } - label={i18n.translate('charts.controls.colorSchema.colorSchemaLabel', { + label={i18n.translate('visDefaultEditor.options.colorSchema.colorSchemaLabel', { defaultMessage: 'Color schema', })} labelAppend={isCustomColors && resetColorsButton} @@ -96,7 +95,7 @@ function ColorSchemaOptions({ ({ const [stateValue, setStateValue] = useState(value); const [isValidState, setIsValidState] = useState(true); - const error = i18n.translate('charts.controls.rangeErrorMessage', { + const error = i18n.translate('visDefaultEditor.options.rangeErrorMessage', { defaultMessage: 'Values must be on or between {min} and {max}', values: { min, max }, }); diff --git a/src/plugins/charts/public/static/components/required_number_input.tsx b/src/plugins/vis_default_editor/public/components/options/required_number_input.tsx similarity index 100% rename from src/plugins/charts/public/static/components/required_number_input.tsx rename to src/plugins/vis_default_editor/public/components/options/required_number_input.tsx diff --git a/src/plugins/charts/public/static/components/select.tsx b/src/plugins/vis_default_editor/public/components/options/select.tsx similarity index 100% rename from src/plugins/charts/public/static/components/select.tsx rename to src/plugins/vis_default_editor/public/components/options/select.tsx diff --git a/src/plugins/charts/public/static/components/switch.tsx b/src/plugins/vis_default_editor/public/components/options/switch.tsx similarity index 100% rename from src/plugins/charts/public/static/components/switch.tsx rename to src/plugins/vis_default_editor/public/components/options/switch.tsx diff --git a/src/plugins/charts/public/static/components/text_input.tsx b/src/plugins/vis_default_editor/public/components/options/text_input.tsx similarity index 100% rename from src/plugins/charts/public/static/components/text_input.tsx rename to src/plugins/vis_default_editor/public/components/options/text_input.tsx diff --git a/src/plugins/vis_default_editor/public/index.ts b/src/plugins/vis_default_editor/public/index.ts index d7eb5eda7bdfe..fd1bdf8b2e65d 100644 --- a/src/plugins/vis_default_editor/public/index.ts +++ b/src/plugins/vis_default_editor/public/index.ts @@ -17,18 +17,19 @@ * under the License. */ -export { DefaultEditorController } from './default_editor_controller'; +import { PluginInitializerContext } from 'kibana/public'; +import { DefaultEditorController } from './default_editor_controller'; +import { VisDefaultEditorPlugin } from './plugin'; + +export { DefaultEditorController }; export { useValidation } from './components/controls/utils'; +export * from './components/options'; export { RangesParamEditor, RangeValues } from './components/controls/ranges'; export * from './editor_size'; export * from './vis_options_props'; export * from './utils'; export { ISchemas, Schemas, Schema } from './schemas'; -/** dummy plugin, we just want visDefaultEditor to have its own bundle */ -export function plugin() { - return new (class VisDefaultEditor { - setup() {} - start() {} - })(); -} +export const plugin = (context: PluginInitializerContext) => { + return new VisDefaultEditorPlugin(); +}; diff --git a/src/plugins/vis_default_editor/public/plugin.ts b/src/plugins/vis_default_editor/public/plugin.ts new file mode 100644 index 0000000000000..a7a5c6146a6e8 --- /dev/null +++ b/src/plugins/vis_default_editor/public/plugin.ts @@ -0,0 +1,40 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { CoreSetup, Plugin } from 'kibana/public'; + +import { VisualizePluginSetup } from '../../visualize/public'; +import { DefaultEditorController } from './default_editor_controller'; + +export interface VisDefaultEditorSetupDependencies { + visualize: VisualizePluginSetup; +} + +export class VisDefaultEditorPlugin + implements Plugin { + public setup(core: CoreSetup, { visualize }: VisDefaultEditorSetupDependencies) { + if (visualize) { + visualize.setDefaultEditor(DefaultEditorController); + } + } + + public start() {} + + stop() {} +} diff --git a/src/plugins/vis_type_markdown/kibana.json b/src/plugins/vis_type_markdown/kibana.json index c0afcb0e99d13..6cfedf60687ef 100644 --- a/src/plugins/vis_type_markdown/kibana.json +++ b/src/plugins/vis_type_markdown/kibana.json @@ -4,5 +4,5 @@ "ui": true, "server": true, "requiredPlugins": ["expressions", "visualizations"], - "requiredBundles": ["kibanaReact", "charts", "visualizations", "expressions", "visDefaultEditor"] + "requiredBundles": ["kibanaReact", "visualizations", "expressions", "visDefaultEditor"] } diff --git a/src/plugins/vis_type_markdown/public/settings_options.tsx b/src/plugins/vis_type_markdown/public/settings_options.tsx index bf4570db5d4a0..1b793ca573f82 100644 --- a/src/plugins/vis_type_markdown/public/settings_options.tsx +++ b/src/plugins/vis_type_markdown/public/settings_options.tsx @@ -21,8 +21,7 @@ import React from 'react'; import { EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; -import { RangeOption, SwitchOption } from '../../charts/public'; +import { VisOptionsProps, SwitchOption, RangeOption } from '../../vis_default_editor/public'; import { MarkdownVisParams } from './types'; function SettingsOptions({ stateParams, setValue }: VisOptionsProps) { diff --git a/src/plugins/vis_type_metric/public/components/metric_vis_options.tsx b/src/plugins/vis_type_metric/public/components/metric_vis_options.tsx index d87a0da740d75..58c486dfa90ab 100644 --- a/src/plugins/vis_type_metric/public/components/metric_vis_options.tsx +++ b/src/plugins/vis_type_metric/public/components/metric_vis_options.tsx @@ -29,16 +29,16 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; import { - ColorMode, ColorRanges, - ColorSchemaOptions, + SetColorRangeValue, + VisOptionsProps, SwitchOption, - RangeOption, SetColorSchemaOptionsValue, - SetColorRangeValue, -} from '../../../charts/public'; + ColorSchemaOptions, + RangeOption, +} from '../../../vis_default_editor/public'; +import { ColorMode } from '../../../charts/public'; import { MetricVisParam, VisParams } from '../types'; function MetricVisOptions({ diff --git a/src/plugins/vis_type_table/kibana.json b/src/plugins/vis_type_table/kibana.json index dce9bce0e8886..1fb8516851ebd 100644 --- a/src/plugins/vis_type_table/kibana.json +++ b/src/plugins/vis_type_table/kibana.json @@ -13,7 +13,6 @@ "kibanaUtils", "kibanaReact", "share", - "charts", "visDefaultEditor" ], "optionalPlugins": ["usageCollection"] diff --git a/src/plugins/vis_type_table/public/components/table_vis_options.tsx b/src/plugins/vis_type_table/public/components/table_vis_options.tsx index b81f0425011da..3932db1262acf 100644 --- a/src/plugins/vis_type_table/public/components/table_vis_options.tsx +++ b/src/plugins/vis_type_table/public/components/table_vis_options.tsx @@ -23,9 +23,13 @@ import { EuiIconTip, EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; import { search } from '../../../data/public'; -import { SwitchOption, SelectOption, NumberInputOption } from '../../../charts/public'; +import { + SwitchOption, + SelectOption, + NumberInputOption, + VisOptionsProps, +} from '../../../vis_default_editor/public'; import { TableVisParams } from '../types'; import { totalAggregations } from './utils'; diff --git a/src/plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx b/src/plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx index d33576e4e5529..5d5f499d650b4 100644 --- a/src/plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx +++ b/src/plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx @@ -20,9 +20,8 @@ import React from 'react'; import { EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { VisOptionsProps } from '../../../vis_default_editor/public'; +import { VisOptionsProps, SelectOption, SwitchOption } from '../../../vis_default_editor/public'; import { ValidatedDualRange } from '../../../kibana_react/public'; -import { SelectOption, SwitchOption } from '../../../charts/public'; import { TagCloudVisParams } from '../types'; function TagCloudOptions({ stateParams, setValue, vis }: VisOptionsProps) { diff --git a/src/plugins/vis_type_timeseries/server/routes/vis.ts b/src/plugins/vis_type_timeseries/server/routes/vis.ts index bba086720da0a..3ed9aaaaea226 100644 --- a/src/plugins/vis_type_timeseries/server/routes/vis.ts +++ b/src/plugins/vis_type_timeseries/server/routes/vis.ts @@ -19,6 +19,7 @@ import { IRouter, KibanaRequest } from 'kibana/server'; import { schema } from '@kbn/config-schema'; +import { ensureNoUnsafeProperties } from '@kbn/std'; import { getVisData, GetVisDataOptions } from '../lib/get_vis_data'; import { visPayloadSchema } from '../../common/vis_schema'; import { ROUTES } from '../../common/constants'; @@ -40,6 +41,14 @@ export const visDataRoutes = ( }, }, async (requestContext, request, response) => { + try { + ensureNoUnsafeProperties(request.body); + } catch (error) { + return response.badRequest({ + body: error.message, + }); + } + try { visPayloadSchema.validate(request.body); } catch (error) { diff --git a/src/plugins/vis_type_vislib/public/editor/components/gauge/labels_panel.tsx b/src/plugins/vis_type_vislib/public/editor/components/gauge/labels_panel.tsx index 0bd5694f71021..e02f62fa6ed6b 100644 --- a/src/plugins/vis_type_vislib/public/editor/components/gauge/labels_panel.tsx +++ b/src/plugins/vis_type_vislib/public/editor/components/gauge/labels_panel.tsx @@ -21,8 +21,7 @@ import React from 'react'; import { EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; - -import { SwitchOption, TextInputOption } from '../../../../../charts/public'; +import { SwitchOption, TextInputOption } from '../../../../../vis_default_editor/public'; import { GaugeOptionsInternalProps } from '../gauge'; function LabelsPanel({ stateParams, setValue, setGaugeValue }: GaugeOptionsInternalProps) { diff --git a/src/plugins/vis_type_vislib/public/editor/components/gauge/ranges_panel.tsx b/src/plugins/vis_type_vislib/public/editor/components/gauge/ranges_panel.tsx index c297fb08e4b68..ec5201af2e7d0 100644 --- a/src/plugins/vis_type_vislib/public/editor/components/gauge/ranges_panel.tsx +++ b/src/plugins/vis_type_vislib/public/editor/components/gauge/ranges_panel.tsx @@ -21,15 +21,13 @@ import React, { useCallback } from 'react'; import { EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; - import { ColorRanges, - ColorSchemaOptions, - ColorSchemaParams, SetColorRangeValue, SwitchOption, - ColorSchemas, -} from '../../../../../charts/public'; + ColorSchemaOptions, +} from '../../../../../vis_default_editor/public'; +import { ColorSchemaParams, ColorSchemas } from '../../../../../charts/public'; import { GaugeOptionsInternalProps } from '../gauge'; import { Gauge } from '../../../gauge'; diff --git a/src/plugins/vis_type_vislib/public/editor/components/gauge/style_panel.tsx b/src/plugins/vis_type_vislib/public/editor/components/gauge/style_panel.tsx index b299b2e86ca40..9cb807aac5759 100644 --- a/src/plugins/vis_type_vislib/public/editor/components/gauge/style_panel.tsx +++ b/src/plugins/vis_type_vislib/public/editor/components/gauge/style_panel.tsx @@ -22,7 +22,7 @@ import { EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { SelectOption } from '../../../../../charts/public'; +import { SelectOption } from '../../../../../vis_default_editor/public'; import { GaugeOptionsInternalProps } from '../gauge'; import { AggGroupNames } from '../../../../../data/public'; diff --git a/src/plugins/vis_type_vislib/public/editor/components/heatmap/index.tsx b/src/plugins/vis_type_vislib/public/editor/components/heatmap/index.tsx index f5b853accb08e..a409762b30f9f 100644 --- a/src/plugins/vis_type_vislib/public/editor/components/heatmap/index.tsx +++ b/src/plugins/vis_type_vislib/public/editor/components/heatmap/index.tsx @@ -23,18 +23,18 @@ import { EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from '../../../../../vis_default_editor/public'; import { ValueAxis } from '../../../../../vis_type_xy/public'; import { + VisOptionsProps, BasicOptions, - ColorRanges, - ColorSchemaOptions, - NumberInputOption, SelectOption, SwitchOption, - SetColorSchemaOptionsValue, + ColorRanges, SetColorRangeValue, -} from '../../../../../charts/public'; + SetColorSchemaOptionsValue, + ColorSchemaOptions, + NumberInputOption, +} from '../../../../../vis_default_editor/public'; import { HeatmapVisParams } from '../../../heatmap'; import { LabelsPanel } from './labels_panel'; diff --git a/src/plugins/vis_type_vislib/public/editor/components/heatmap/labels_panel.tsx b/src/plugins/vis_type_vislib/public/editor/components/heatmap/labels_panel.tsx index 8ec06ea50ec12..506e5f74dc972 100644 --- a/src/plugins/vis_type_vislib/public/editor/components/heatmap/labels_panel.tsx +++ b/src/plugins/vis_type_vislib/public/editor/components/heatmap/labels_panel.tsx @@ -23,8 +23,7 @@ import { EuiColorPicker, EuiFormRow, EuiPanel, EuiSpacer, EuiTitle } from '@elas import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from '../../../../../vis_default_editor/public'; -import { SwitchOption } from '../../../../../charts/public'; +import { VisOptionsProps, SwitchOption } from '../../../../../vis_default_editor/public'; import { ValueAxis } from '../../../../../vis_type_xy/public'; import { HeatmapVisParams } from '../../../heatmap'; diff --git a/src/plugins/vis_type_vislib/public/editor/components/pie.tsx b/src/plugins/vis_type_vislib/public/editor/components/pie.tsx index 1c3aa501b4d00..01516630287ec 100644 --- a/src/plugins/vis_type_vislib/public/editor/components/pie.tsx +++ b/src/plugins/vis_type_vislib/public/editor/components/pie.tsx @@ -22,8 +22,7 @@ import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from '../../../../vis_default_editor/public'; -import { BasicOptions, SwitchOption } from '../../../../charts/public'; +import { BasicOptions, SwitchOption, VisOptionsProps } from '../../../../vis_default_editor/public'; import { TruncateLabelsOption } from '../../../../vis_type_xy/public'; import { PieVisParams } from '../../pie'; diff --git a/src/plugins/vis_type_vislib/public/plugin.ts b/src/plugins/vis_type_vislib/public/plugin.ts index 36a184d3da507..0f849c1833230 100644 --- a/src/plugins/vis_type_vislib/public/plugin.ts +++ b/src/plugins/vis_type_vislib/public/plugin.ts @@ -61,7 +61,7 @@ export class VisTypeVislibPlugin core: VisTypeVislibCoreSetup, { expressions, visualizations, charts }: VisTypeVislibPluginSetupDependencies ) { - if (!core.uiSettings.get(LEGACY_CHARTS_LIBRARY, true)) { + if (!core.uiSettings.get(LEGACY_CHARTS_LIBRARY, false)) { // Register only non-replaced vis types convertedTypeDefinitions.forEach(visualizations.createBaseVisualization); visualizations.createBaseVisualization(pieVisTypeDefinition); diff --git a/src/plugins/vis_type_vislib/public/vis_wrapper.tsx b/src/plugins/vis_type_vislib/public/vis_wrapper.tsx index b8dbd0f945c32..e2e8a98a9a8b6 100644 --- a/src/plugins/vis_type_vislib/public/vis_wrapper.tsx +++ b/src/plugins/vis_type_vislib/public/vis_wrapper.tsx @@ -61,7 +61,7 @@ const VislibWrapper = ({ core, charts, visData, visConfig, handlers }: VislibWra visController.current?.destroy(); visController.current = null; }; - }, [core, charts, handlers]); + }, [core, charts]); useEffect(updateChart, [updateChart]); diff --git a/src/plugins/vis_type_xy/kibana.json b/src/plugins/vis_type_xy/kibana.json index 14c3ce36bf375..619fa8e71c0dd 100644 --- a/src/plugins/vis_type_xy/kibana.json +++ b/src/plugins/vis_type_xy/kibana.json @@ -3,6 +3,6 @@ "version": "kibana", "server": true, "ui": true, - "requiredPlugins": ["charts", "data", "expressions", "visualizations"], + "requiredPlugins": ["charts", "data", "expressions", "visualizations", "usageCollection"], "requiredBundles": ["kibanaUtils", "visDefaultEditor"] } diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/category_axis_panel.tsx b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/category_axis_panel.tsx index a551163747526..d4647ae41a637 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/category_axis_panel.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/category_axis_panel.tsx @@ -24,8 +24,11 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui'; import { Position } from '@elastic/charts'; -import { SelectOption, SwitchOption } from '../../../../../../charts/public'; -import { VisOptionsProps } from '../../../../../../vis_default_editor/public'; +import { + SelectOption, + SwitchOption, + VisOptionsProps, +} from '../../../../../../vis_default_editor/public'; import { LabelOptions, SetAxisLabel } from './label_options'; import { CategoryAxis } from '../../../../types'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/chart_options.tsx b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/chart_options.tsx index c379fa30b49b8..070d5fe018150 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/chart_options.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/chart_options.tsx @@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { Vis } from '../../../../../../visualizations/public'; -import { SelectOption } from '../../../../../../charts/public'; +import { SelectOption } from '../../../../../../vis_default_editor/public'; import { SeriesParam, ValueAxis } from '../../../../types'; import { LineOptions } from './line_options'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/custom_extents_options.tsx b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/custom_extents_options.tsx index 86a0c56e46942..f64bdba542b99 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/custom_extents_options.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/custom_extents_options.tsx @@ -21,7 +21,7 @@ import React, { useCallback, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; -import { NumberInputOption, SwitchOption } from '../../../../../../charts/public'; +import { NumberInputOption, SwitchOption } from '../../../../../../vis_default_editor/public'; import { ValueAxis } from '../../../../types'; import { YExtents } from './y_extents'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/label_options.tsx b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/label_options.tsx index 8c5c440ad9de9..bc00e3768aed6 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/label_options.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/label_options.tsx @@ -23,7 +23,8 @@ import { EuiTitle, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { SelectOption, SwitchOption, Labels } from '../../../../../../charts/public'; +import { SelectOption, SwitchOption } from '../../../../../../vis_default_editor/public'; +import { Labels } from '../../../../../../charts/public'; import { TruncateLabelsOption } from '../../common'; import { getRotateOptions } from '../../../collections'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/line_options.test.tsx b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/line_options.test.tsx index 7727f90f79107..c4a8fea510f82 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/line_options.test.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/line_options.test.tsx @@ -20,7 +20,7 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { NumberInputOption } from '../../../../../../charts/public'; +import { NumberInputOption } from '../../../../../../vis_default_editor/public'; import { LineOptions, LineOptionsParams } from './line_options'; import { seriesParam, vis } from './mocks'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/line_options.tsx b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/line_options.tsx index df2735396b38d..39a2ad8de95fd 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/line_options.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/line_options.tsx @@ -23,7 +23,11 @@ import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { Vis } from '../../../../../../visualizations/public'; -import { NumberInputOption, SelectOption, SwitchOption } from '../../../../../../charts/public'; +import { + NumberInputOption, + SelectOption, + SwitchOption, +} from '../../../../../../vis_default_editor/public'; import { SeriesParam } from '../../../../types'; import { SetChart } from './chart_options'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axis_options.test.tsx b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axis_options.test.tsx index 0b325198c3fe7..62757d14a0196 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axis_options.test.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axis_options.test.tsx @@ -22,7 +22,7 @@ import { shallow } from 'enzyme'; import { Position } from '@elastic/charts'; -import { TextInputOption } from '../../../../../../charts/public'; +import { TextInputOption } from '../../../../../../vis_default_editor/public'; import { ValueAxis, ScaleType } from '../../../../types'; import { LabelOptions } from './label_options'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axis_options.tsx b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axis_options.tsx index 4ab792142e83a..d81ddcb95ce62 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axis_options.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axis_options.tsx @@ -22,7 +22,11 @@ import { i18n } from '@kbn/i18n'; import { EuiSpacer, EuiAccordion, EuiHorizontalRule } from '@elastic/eui'; import { Vis } from '../../../../../../visualizations/public'; -import { SelectOption, SwitchOption, TextInputOption } from '../../../../../../charts/public'; +import { + SelectOption, + SwitchOption, + TextInputOption, +} from '../../../../../../vis_default_editor/public'; import { ValueAxis } from '../../../../types'; import { LabelOptions, SetAxisLabel } from './label_options'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/y_extents.test.tsx b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/y_extents.test.tsx index c2af7f2ad921b..27a28d96d0608 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/y_extents.test.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/y_extents.test.tsx @@ -20,10 +20,9 @@ import React from 'react'; import { mount, shallow } from 'enzyme'; -import { NumberInputOption } from '../../../../../../charts/public'; - import { ScaleType } from '../../../../types'; import { YExtents, YExtentsProps } from './y_extents'; +import { NumberInputOption } from '../../../../../../vis_default_editor/public'; describe('YExtents component', () => { let setMultipleValidity: jest.Mock; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/y_extents.tsx b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/y_extents.tsx index 11d049d4864a7..ba7049e984573 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/y_extents.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/y_extents.tsx @@ -21,7 +21,7 @@ import React, { useEffect, useCallback } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { NumberInputOption } from '../../../../../../charts/public'; +import { NumberInputOption } from '../../../../../../vis_default_editor/public'; import { Scale, ScaleType } from '../../../../types'; import { SetScale } from './value_axis_options'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/point_series/elastic_charts_options.tsx b/src/plugins/vis_type_xy/public/editor/components/options/point_series/elastic_charts_options.tsx index 126c5521f0633..a3e573741644c 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/point_series/elastic_charts_options.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/point_series/elastic_charts_options.tsx @@ -20,14 +20,17 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; +import { METRIC_TYPE } from '@kbn/analytics'; -import { SelectOption, SwitchOption } from '../../../../../../charts/public'; +import { SelectOption, SwitchOption } from '../../../../../../vis_default_editor/public'; import { ChartType } from '../../../../../common'; import { VisParams } from '../../../../types'; import { ValidationVisOptionsProps } from '../../common'; +import { getTrackUiMetric } from '../../../../services'; export function ElasticChartsOptions(props: ValidationVisOptionsProps) { + const trackUiMetric = getTrackUiMetric(); const { stateParams, setValue, vis, aggs } = props; const hasLineChart = stateParams.seriesParams.some( @@ -49,7 +52,12 @@ export function ElasticChartsOptions(props: ValidationVisOptionsProps })} paramName="detailedTooltip" value={stateParams.detailedTooltip} - setValue={setValue} + setValue={(paramName, value) => { + if (trackUiMetric) { + trackUiMetric(METRIC_TYPE.CLICK, 'detailed_tooltip_switched'); + } + setValue(paramName, value); + }} /> {hasLineChart && ( @@ -61,7 +69,12 @@ export function ElasticChartsOptions(props: ValidationVisOptionsProps options={vis.type.editorConfig.collections.fittingFunctions} paramName="fittingFunction" value={stateParams.fittingFunction} - setValue={setValue} + setValue={(paramName, value) => { + if (trackUiMetric) { + trackUiMetric(METRIC_TYPE.CLICK, 'fitting_function_selected'); + } + setValue(paramName, value); + }} /> )} diff --git a/src/plugins/vis_type_xy/public/editor/components/options/point_series/grid_panel.tsx b/src/plugins/vis_type_xy/public/editor/components/options/point_series/grid_panel.tsx index c6ad52f7112c9..9efc9b65b19ee 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/point_series/grid_panel.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/point_series/grid_panel.tsx @@ -23,8 +23,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui'; -import { SelectOption, SwitchOption } from '../../../../../../charts/public'; - +import { SelectOption, SwitchOption } from '../../../../../../vis_default_editor/public'; import { VisParams, ValueAxis } from '../../../../types'; import { ValidationVisOptionsProps } from '../../common'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.tsx b/src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.tsx index 283fc28aed46e..1d00f80e0b0d7 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.tsx @@ -22,7 +22,7 @@ import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { BasicOptions, SwitchOption } from '../../../../../../charts/public'; +import { BasicOptions, SwitchOption } from '../../../../../../vis_default_editor/public'; import { BUCKET_TYPES } from '../../../../../../data/public'; import { VisParams } from '../../../../types'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/point_series/threshold_panel.tsx b/src/plugins/vis_type_xy/public/editor/components/options/point_series/threshold_panel.tsx index ec21a386a5679..8eab0c478e67b 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/point_series/threshold_panel.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/point_series/threshold_panel.tsx @@ -26,8 +26,7 @@ import { SelectOption, SwitchOption, RequiredNumberInputOption, -} from '../../../../../../charts/public'; - +} from '../../../../../../vis_default_editor/public'; import { ValidationVisOptionsProps } from '../../common'; import { VisParams } from '../../../../types'; diff --git a/src/plugins/vis_type_xy/public/plugin.ts b/src/plugins/vis_type_xy/public/plugin.ts index 7425c5f7248ac..ab22ae57ebbdf 100644 --- a/src/plugins/vis_type_xy/public/plugin.ts +++ b/src/plugins/vis_type_xy/public/plugin.ts @@ -22,6 +22,7 @@ import { Plugin as ExpressionsPublicPlugin } from '../../expressions/public'; import { VisualizationsSetup, VisualizationsStart } from '../../visualizations/public'; import { ChartsPluginSetup } from '../../charts/public'; import { DataPublicPluginStart } from '../../data/public'; +import { UsageCollectionSetup } from '../../usage_collection/public'; import { createVisTypeXyVisFn } from './xy_vis_fn'; import { @@ -32,6 +33,7 @@ import { setTimefilter, setUISettings, setDocLinks, + setTrackUiMetric, } from './services'; import { visTypesDefinitions } from './vis_types'; import { LEGACY_CHARTS_LIBRARY } from '../common'; @@ -47,6 +49,7 @@ export interface VisTypeXyPluginSetupDependencies { expressions: ReturnType; visualizations: VisualizationsSetup; charts: ChartsPluginSetup; + usageCollection: UsageCollectionSetup; } /** @internal */ @@ -69,9 +72,9 @@ export class VisTypeXyPlugin > { public async setup( core: VisTypeXyCoreSetup, - { expressions, visualizations, charts }: VisTypeXyPluginSetupDependencies + { expressions, visualizations, charts, usageCollection }: VisTypeXyPluginSetupDependencies ) { - if (!core.uiSettings.get(LEGACY_CHARTS_LIBRARY, true)) { + if (!core.uiSettings.get(LEGACY_CHARTS_LIBRARY, false)) { setUISettings(core.uiSettings); setThemeService(charts.theme); setColorsService(charts.legacyColors); @@ -81,6 +84,8 @@ export class VisTypeXyPlugin visTypesDefinitions.forEach(visualizations.createBaseVisualization); } + setTrackUiMetric(usageCollection?.reportUiCounter.bind(usageCollection, 'vis_type_xy')); + return {}; } diff --git a/src/plugins/vis_type_xy/public/services.ts b/src/plugins/vis_type_xy/public/services.ts index 5a72759ecff6c..086cab8fb217a 100644 --- a/src/plugins/vis_type_xy/public/services.ts +++ b/src/plugins/vis_type_xy/public/services.ts @@ -17,6 +17,7 @@ * under the License. */ +import { UiCounterMetricType } from '@kbn/analytics'; import { CoreSetup, DocLinksStart } from '../../../core/public'; import { createGetterSetter } from '../../kibana_utils/public'; import { DataPublicPluginStart } from '../../data/public'; @@ -47,3 +48,7 @@ export const [getColorsService, setColorsService] = createGetterSetter< >('xy charts.color'); export const [getDocLinks, setDocLinks] = createGetterSetter('DocLinks'); + +export const [getTrackUiMetric, setTrackUiMetric] = createGetterSetter< + (metricType: UiCounterMetricType, eventName: string | string[]) => void +>('trackUiMetric'); diff --git a/src/plugins/vis_type_xy/server/plugin.ts b/src/plugins/vis_type_xy/server/plugin.ts index b5999535064aa..fafc4052a88fa 100644 --- a/src/plugins/vis_type_xy/server/plugin.ts +++ b/src/plugins/vis_type_xy/server/plugin.ts @@ -31,7 +31,7 @@ export const uiSettingsConfig: Record> = { name: i18n.translate('visTypeXy.advancedSettings.visualization.legacyChartsLibrary.name', { defaultMessage: 'Legacy charts library', }), - value: true, + value: false, description: i18n.translate( 'visTypeXy.advancedSettings.visualization.legacyChartsLibrary.description', { diff --git a/src/plugins/visualize/kibana.json b/src/plugins/visualize/kibana.json index 27229a11cd99f..7f5c7d0dc08a2 100644 --- a/src/plugins/visualize/kibana.json +++ b/src/plugins/visualize/kibana.json @@ -22,8 +22,7 @@ "kibanaUtils", "kibanaReact", "home", - "discover", - "visDefaultEditor", - "presentationUtil" + "presentationUtil", + "discover" ] } diff --git a/src/plugins/visualize/public/application/utils/use/use_saved_vis_instance.test.ts b/src/plugins/visualize/public/application/utils/use/use_saved_vis_instance.test.ts index 3f9676a9c9385..3995ebfd37253 100644 --- a/src/plugins/visualize/public/application/utils/use/use_saved_vis_instance.test.ts +++ b/src/plugins/visualize/public/application/utils/use/use_saved_vis_instance.test.ts @@ -26,6 +26,7 @@ import { redirectWhenMissing } from '../../../../../kibana_utils/public'; import { getEditBreadcrumbs, getCreateBreadcrumbs } from '../breadcrumbs'; import { VisualizeServices } from '../../types'; import { VisualizeConstants } from '../../visualize_constants'; +import { setDefaultEditor } from '../../../services'; const mockDefaultEditorControllerDestroy = jest.fn(); const mockEmbeddableHandlerDestroy = jest.fn(); @@ -54,10 +55,14 @@ jest.mock('../breadcrumbs', () => ({ getEditBreadcrumbs: jest.fn((text) => text), getCreateBreadcrumbs: jest.fn((text) => text), })); -jest.mock('../../../../../vis_default_editor/public', () => ({ - DefaultEditorController: jest.fn(() => ({ destroy: mockDefaultEditorControllerDestroy })), -})); -jest.mock('../../../../../kibana_utils/public'); + +jest.mock('../../../../../kibana_utils/public', () => { + const actual = jest.requireActual('../../../../../kibana_utils/public'); + return { + ...actual, + redirectWhenMissing: jest.fn(), + }; +}); const mockGetVisualizationInstance = jest.requireMock('../get_visualization_instance') .getVisualizationInstance; @@ -69,6 +74,10 @@ describe('useSavedVisInstance', () => { const eventEmitter = new EventEmitter(); beforeEach(() => { + setDefaultEditor( + jest.fn().mockImplementation(() => ({ destroy: mockDefaultEditorControllerDestroy })) + ); + mockServices = ({ ...coreStartMock, toastNotifications, diff --git a/src/plugins/visualize/public/application/utils/use/use_saved_vis_instance.ts b/src/plugins/visualize/public/application/utils/use/use_saved_vis_instance.ts index 44fbcce82f458..9c156f20be375 100644 --- a/src/plugins/visualize/public/application/utils/use/use_saved_vis_instance.ts +++ b/src/plugins/visualize/public/application/utils/use/use_saved_vis_instance.ts @@ -23,12 +23,12 @@ import { parse } from 'query-string'; import { i18n } from '@kbn/i18n'; import { redirectWhenMissing } from '../../../../../kibana_utils/public'; -import { DefaultEditorController } from '../../../../../vis_default_editor/public'; import { getVisualizationInstance } from '../get_visualization_instance'; import { getEditBreadcrumbs, getCreateBreadcrumbs } from '../breadcrumbs'; import { SavedVisInstance, IEditorController, VisualizeServices } from '../../types'; import { VisualizeConstants } from '../../visualize_constants'; +import { getDefaultEditor } from '../../../services'; /** * This effect is responsible for instantiating a saved vis or creating a new one @@ -104,7 +104,7 @@ export const useSavedVisInstance = ( // do not create editor in embeded mode if (visEditorRef.current) { if (isChromeVisible) { - const Editor = vis.type.editor || DefaultEditorController; + const Editor = vis.type.editor || getDefaultEditor(); visEditorController = new Editor( visEditorRef.current, vis, diff --git a/src/plugins/visualize/public/application/utils/use/use_vis_byvalue.ts b/src/plugins/visualize/public/application/utils/use/use_vis_byvalue.ts index e0286a63b9feb..ec97c22183940 100644 --- a/src/plugins/visualize/public/application/utils/use/use_vis_byvalue.ts +++ b/src/plugins/visualize/public/application/utils/use/use_vis_byvalue.ts @@ -23,7 +23,7 @@ import { VisualizeInput } from 'src/plugins/visualizations/public'; import { ByValueVisInstance, IEditorController, VisualizeServices } from '../../types'; import { getVisualizationInstanceFromInput } from '../get_visualization_instance'; import { getBreadcrumbsPrefixedWithApp, getEditBreadcrumbs } from '../breadcrumbs'; -import { DefaultEditorController } from '../../../../../vis_default_editor/public'; +import { getDefaultEditor } from '../../../services'; export const useVisByValue = ( services: VisualizeServices, @@ -46,7 +46,8 @@ export const useVisByValue = ( } const byValueVisInstance = await getVisualizationInstanceFromInput(services, valueInput); const { embeddableHandler, vis } = byValueVisInstance; - const Editor = vis.type.editor || DefaultEditorController; + + const Editor = vis.type.editor || getDefaultEditor(); const visEditorController = new Editor( visEditorRef.current, vis, diff --git a/src/plugins/visualize/public/index.ts b/src/plugins/visualize/public/index.ts index 246806f300800..c9ac85c5123ce 100644 --- a/src/plugins/visualize/public/index.ts +++ b/src/plugins/visualize/public/index.ts @@ -18,7 +18,7 @@ */ import { PluginInitializerContext } from 'kibana/public'; -import { VisualizePlugin } from './plugin'; +import { VisualizePlugin, VisualizePluginSetup } from './plugin'; export type { EditorRenderProps, @@ -27,6 +27,8 @@ export type { } from './application/types'; export { VisualizeConstants } from './application/visualize_constants'; +export { VisualizePluginSetup }; + export const plugin = (context: PluginInitializerContext) => { return new VisualizePlugin(context); }; diff --git a/src/plugins/visualize/public/plugin.ts b/src/plugins/visualize/public/plugin.ts index bbd7be0d34883..173f3fbbb6363 100644 --- a/src/plugins/visualize/public/plugin.ts +++ b/src/plugins/visualize/public/plugin.ts @@ -44,7 +44,7 @@ import { UrlForwardingSetup, UrlForwardingStart } from '../../url_forwarding/pub import { VisualizationsStart } from '../../visualizations/public'; import { VisualizeConstants } from './application/visualize_constants'; import { FeatureCatalogueCategory, HomePublicPluginSetup } from '../../home/public'; -import { VisualizeServices } from './application/types'; +import { VisEditorConstructor, VisualizeServices } from './application/types'; import { DEFAULT_APP_CATEGORIES } from '../../../core/public'; import { SavedObjectsStart } from '../../saved_objects/public'; import { EmbeddableStart } from '../../embeddable/public'; @@ -57,6 +57,7 @@ import { setIndexPatterns, setQueryService, setShareService, + setDefaultEditor, } from './services'; import { visualizeFieldAction } from './actions/visualize_field_action'; import { createVisualizeUrlGenerator } from './url_generator'; @@ -81,9 +82,18 @@ export interface VisualizePluginSetupDependencies { uiActions: UiActionsSetup; } +export interface VisualizePluginSetup { + setDefaultEditor: (editor: VisEditorConstructor) => void; +} + export class VisualizePlugin implements - Plugin { + Plugin< + VisualizePluginSetup, + void, + VisualizePluginSetupDependencies, + VisualizePluginStartDependencies + > { private appStateUpdater = new BehaviorSubject(() => ({})); private stopUrlTracking: (() => void) | undefined = undefined; private currentHistory: ScopedHistory | undefined = undefined; @@ -231,6 +241,12 @@ export class VisualizePlugin category: FeatureCatalogueCategory.DATA, }); } + + return { + setDefaultEditor: (editor) => { + setDefaultEditor(editor); + }, + } as VisualizePluginSetup; } public start(core: CoreStart, plugins: VisualizePluginStartDependencies) { diff --git a/src/plugins/visualize/public/services.ts b/src/plugins/visualize/public/services.ts index 8190872ec6508..7994ad14543d5 100644 --- a/src/plugins/visualize/public/services.ts +++ b/src/plugins/visualize/public/services.ts @@ -21,6 +21,7 @@ import { ApplicationStart, IUiSettingsClient } from '../../../core/public'; import { createGetterSetter } from '../../../plugins/kibana_utils/public'; import { IndexPatternsContract, DataPublicPluginStart } from '../../../plugins/data/public'; import { SharePluginStart } from '../../../plugins/share/public'; +import { VisEditorConstructor } from './application/types'; export const [getUISettings, setUISettings] = createGetterSetter('UISettings'); @@ -32,6 +33,10 @@ export const [getIndexPatterns, setIndexPatterns] = createGetterSetter( + 'DefaultEditor' +); + export const [getQueryService, setQueryService] = createGetterSetter< DataPublicPluginStart['query'] >('Query'); diff --git a/tasks/check_plugins.js b/tasks/check_plugins.js deleted file mode 100644 index 20fb8a895af6c..0000000000000 --- a/tasks/check_plugins.js +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import fs from 'fs'; -import path from 'path'; - -export default function checkPlugins(grunt) { - grunt.registerTask( - 'checkPlugins', - 'Checks for plugins which may disrupt tests', - function checkPlugins() { - const done = this.async(); - const pluginsDir = path.resolve('./plugins/'); - - fs.readdir(pluginsDir, (err, files) => { - if (!files) { - return done(); - } - - const plugins = files.filter((file) => { - return fs.statSync(path.join(pluginsDir, file)).isDirectory(); - }); - - if (plugins.length) { - grunt.log.error( - '===================================================================================================' - ); - plugins.forEach((plugin) => { - grunt.log.error( - `The ${plugin} plugin may disrupt the test process. Consider removing it and re-running your tests.` - ); - }); - grunt.log.error( - '===================================================================================================' - ); - } - - done(); - }); - } - ); -} diff --git a/tasks/docker_docs.js b/tasks/docker_docs.js deleted file mode 100644 index 3a2041abc9301..0000000000000 --- a/tasks/docker_docs.js +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import del from 'del'; -import { join } from 'path'; -import { execFileSync as exec } from 'child_process'; - -export default function (grunt) { - grunt.registerTask('docker:docs', 'Build docs from docker', function () { - const rootPath = grunt.config.get('root'); - const composePath = join(rootPath, 'tasks/docker_docs/docker-compose.yml'); - const htmlDocsDir = join(rootPath, 'html_docs'); - - const env = Object.assign(process.env, { - KIBANA_DOCS_CONTAINER_NAME: 'kibana_docs', - KIBANA_DOCS_CONTEXT: rootPath, - }); - const stdio = [0, 1, 2]; - const execOptions = { env, stdio }; - - exec('docker-compose', ['-f', composePath, 'up'], execOptions); - - const containerId = String( - exec('docker-compose', ['-f', composePath, 'ps', '-q', env.KIBANA_DOCS_CONTAINER_NAME], { - env, - }) - ).trim(); - - grunt.log.write('Clearing old docs ... '); - del.sync(htmlDocsDir); - grunt.log.writeln('done'); - - grunt.log.write('Copying new docs ... '); - exec('docker', ['cp', `${containerId}:/home/kibana/html_docs`, htmlDocsDir]); - grunt.log.writeln('done'); - }); -} diff --git a/tasks/docker_docs/Dockerfile b/tasks/docker_docs/Dockerfile deleted file mode 100644 index 435b78f89f2e7..0000000000000 --- a/tasks/docker_docs/Dockerfile +++ /dev/null @@ -1,18 +0,0 @@ -FROM alpine:3.6 - -RUN apk add --no-cache \ - git \ - libxslt \ - curl \ - python \ - libxml2-utils \ - perl - -RUN addgroup kibana && \ - adduser -D -G kibana -s /bin/bash -h /home/kibana kibana - -USER kibana -RUN git clone --depth 1 https://github.com/elastic/docs.git /home/kibana/docs_builder - -WORKDIR /home/kibana/docs_builder -CMD git pull origin master && ./build_docs.pl --doc /home/kibana/ascii_docs/index.asciidoc --out /home/kibana/html_docs --chunk=1 diff --git a/tasks/docker_docs/docker-compose.yml b/tasks/docker_docs/docker-compose.yml deleted file mode 100644 index 8d0bd2c136596..0000000000000 --- a/tasks/docker_docs/docker-compose.yml +++ /dev/null @@ -1,8 +0,0 @@ -version: '2' -services: - kibana_docs: - container_name: $KIBANA_DOCS_CONTAINER_NAME - build: - context: $KIBANA_DOCS_CONTEXT/tasks/docker_docs - volumes: - - $KIBANA_DOCS_CONTEXT/docs:/home/kibana/ascii_docs diff --git a/test/functional/apps/dashboard/embeddable_data_grid.ts b/test/functional/apps/dashboard/embeddable_data_grid.ts new file mode 100644 index 0000000000000..067536ab7aa93 --- /dev/null +++ b/test/functional/apps/dashboard/embeddable_data_grid.ts @@ -0,0 +1,60 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const dashboardAddPanel = getService('dashboardAddPanel'); + const filterBar = getService('filterBar'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const find = getService('find'); + const PageObjects = getPageObjects(['common', 'dashboard', 'header', 'timePicker', 'discover']); + + describe('dashboard embeddable data grid', () => { + before(async () => { + await esArchiver.loadIfNeeded('logstash_functional'); + await esArchiver.loadIfNeeded('dashboard/current/data'); + await esArchiver.loadIfNeeded('dashboard/current/kibana'); + await kibanaServer.uiSettings.replace({ + defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', + 'doc_table:legacy': false, + }); + await PageObjects.common.navigateToApp('dashboard'); + await filterBar.ensureFieldEditorModalIsClosed(); + await PageObjects.dashboard.gotoDashboardLandingPage(); + await PageObjects.dashboard.clickNewDashboard(); + await PageObjects.timePicker.setDefaultDataRange(); + }); + + describe('saved search filters', function () { + it('are added when a cell filter is clicked', async function () { + await dashboardAddPanel.addSavedSearch('Rendering-Test:-saved-search'); + await find.clickByCssSelector(`[role="gridcell"]:nth-child(2)`); + await find.clickByCssSelector(`[data-test-subj="filterOutButton"]`); + await PageObjects.header.waitUntilLoadingHasFinished(); + await find.clickByCssSelector(`[role="gridcell"]:nth-child(2)`); + await find.clickByCssSelector(`[data-test-subj="filterForButton"]`); + const filterCount = await filterBar.getFilterCount(); + expect(filterCount).to.equal(2); + }); + }); + }); +} diff --git a/test/functional/apps/dashboard/index.ts b/test/functional/apps/dashboard/index.ts index 6fb5f874022a0..43ad1aad5de00 100644 --- a/test/functional/apps/dashboard/index.ts +++ b/test/functional/apps/dashboard/index.ts @@ -54,6 +54,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./empty_dashboard')); loadTestFile(require.resolve('./url_field_formatter')); loadTestFile(require.resolve('./embeddable_rendering')); + loadTestFile(require.resolve('./embeddable_data_grid')); loadTestFile(require.resolve('./create_and_add_embeddables')); loadTestFile(require.resolve('./edit_embeddable_redirects')); loadTestFile(require.resolve('./edit_visualizations')); diff --git a/test/functional/apps/discover/_data_grid.ts b/test/functional/apps/discover/_data_grid.ts new file mode 100644 index 0000000000000..8f62e03518253 --- /dev/null +++ b/test/functional/apps/discover/_data_grid.ts @@ -0,0 +1,67 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import expect from '@kbn/expect'; + +export default function ({ + getService, + getPageObjects, +}: { + getService: (service: string) => any; + getPageObjects: (pageObjects: string[]) => any; +}) { + describe('discover data grid tests', function describeDiscoverDataGrid() { + const esArchiver = getService('esArchiver'); + const PageObjects = getPageObjects(['common', 'discover', 'timePicker']); + const kibanaServer = getService('kibanaServer'); + const defaultSettings = { defaultIndex: 'logstash-*', 'doc_table:legacy': false }; + const testSubjects = getService('testSubjects'); + + before(async function () { + await esArchiver.load('discover'); + await esArchiver.loadIfNeeded('logstash_functional'); + await kibanaServer.uiSettings.replace(defaultSettings); + await PageObjects.common.navigateToApp('discover'); + await PageObjects.timePicker.setDefaultAbsoluteRange(); + }); + + after(async function () { + await kibanaServer.uiSettings.replace({ 'doc_table:legacy': true }); + }); + + it('can add fields to the table', async function () { + const getTitles = async () => + (await testSubjects.getVisibleText('dataGridHeader')).replace(/\s|\r?\n|\r/g, ' '); + + expect(await getTitles()).to.be('Time (@timestamp) _source'); + + await PageObjects.discover.clickFieldListItemAdd('bytes'); + expect(await getTitles()).to.be('Time (@timestamp) bytes'); + + await PageObjects.discover.clickFieldListItemAdd('agent'); + expect(await getTitles()).to.be('Time (@timestamp) bytes agent'); + + await PageObjects.discover.clickFieldListItemAdd('bytes'); + expect(await getTitles()).to.be('Time (@timestamp) agent'); + + await PageObjects.discover.clickFieldListItemAdd('agent'); + expect(await getTitles()).to.be('Time (@timestamp) _source'); + }); + }); +} diff --git a/test/functional/apps/discover/_data_grid_context.ts b/test/functional/apps/discover/_data_grid_context.ts new file mode 100644 index 0000000000000..6821b9c69cf7e --- /dev/null +++ b/test/functional/apps/discover/_data_grid_context.ts @@ -0,0 +1,91 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +const TEST_COLUMN_NAMES = ['@message']; +const TEST_FILTER_COLUMN_NAMES = [ + ['extension', 'jpg'], + ['geo.src', 'IN'], +]; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const retry = getService('retry'); + const filterBar = getService('filterBar'); + const dataGrid = getService('dataGrid'); + const docTable = getService('docTable'); + const PageObjects = getPageObjects(['common', 'discover', 'timePicker', 'settings']); + const defaultSettings = { defaultIndex: 'logstash-*', 'doc_table:legacy': false }; + const kibanaServer = getService('kibanaServer'); + const esArchiver = getService('esArchiver'); + + describe('discover data grid context tests', () => { + before(async () => { + await esArchiver.loadIfNeeded('logstash_functional'); + await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); + await kibanaServer.uiSettings.update(defaultSettings); + await PageObjects.common.navigateToApp('discover'); + + for (const columnName of TEST_COLUMN_NAMES) { + await PageObjects.discover.clickFieldListItemAdd(columnName); + } + + for (const [columnName, value] of TEST_FILTER_COLUMN_NAMES) { + await PageObjects.discover.clickFieldListItem(columnName); + await PageObjects.discover.clickFieldListPlusFilter(columnName, value); + } + }); + after(async () => { + await PageObjects.timePicker.resetDefaultAbsoluteRangeViaUiSettings(); + }); + + it('should open the context view with the selected document as anchor', async () => { + // check the anchor timestamp in the context view + await retry.waitFor('selected document timestamp matches anchor timestamp ', async () => { + // get the timestamp of the first row + const discoverFields = await dataGrid.getFields(); + const firstTimestamp = discoverFields[0][0]; + + // navigate to the context view + await dataGrid.clickRowToggle({ rowIndex: 0 }); + const rowActions = await dataGrid.getRowActions({ rowIndex: 0 }); + await rowActions[1].click(); + // entering the context view (contains the legacy type) + const contextFields = await docTable.getFields(); + const anchorTimestamp = contextFields[0][0]; + return anchorTimestamp === firstTimestamp; + }); + }); + + it('should open the context view with the same columns', async () => { + const columnNames = await docTable.getHeaderFields(); + expect(columnNames).to.eql(['Time', ...TEST_COLUMN_NAMES]); + }); + + it('should open the context view with the filters disabled', async () => { + let disabledFilterCounter = 0; + for (const [columnName, value] of TEST_FILTER_COLUMN_NAMES) { + if (await filterBar.hasFilter(columnName, value, false)) { + disabledFilterCounter++; + } + } + expect(disabledFilterCounter).to.be(TEST_FILTER_COLUMN_NAMES.length); + }); + }); +} diff --git a/test/functional/apps/discover/_data_grid_doc_navigation.ts b/test/functional/apps/discover/_data_grid_doc_navigation.ts new file mode 100644 index 0000000000000..92d9893cab0b6 --- /dev/null +++ b/test/functional/apps/discover/_data_grid_doc_navigation.ts @@ -0,0 +1,91 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const filterBar = getService('filterBar'); + const dataGrid = getService('dataGrid'); + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['common', 'discover', 'timePicker', 'context']); + const esArchiver = getService('esArchiver'); + const retry = getService('retry'); + const kibanaServer = getService('kibanaServer'); + const defaultSettings = { defaultIndex: 'logstash-*', 'doc_table:legacy': false }; + + describe('discover data grid doc link', function () { + beforeEach(async function () { + await esArchiver.loadIfNeeded('logstash_functional'); + await esArchiver.loadIfNeeded('discover'); + await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); + await kibanaServer.uiSettings.update(defaultSettings); + await PageObjects.common.navigateToApp('discover'); + }); + + it('should open the doc view of the selected document', async function () { + // navigate to the doc view + await dataGrid.clickRowToggle({ rowIndex: 0 }); + + // click the open action + await retry.try(async () => { + const rowActions = await dataGrid.getRowActions({ rowIndex: 0 }); + if (!rowActions.length) { + throw new Error('row actions empty, trying again'); + } + await rowActions[0].click(); + }); + + const hasDocHit = await testSubjects.exists('doc-hit'); + expect(hasDocHit).to.be(true); + }); + + it('add filter should create an exists filter if value is null (#7189)', async function () { + await PageObjects.discover.waitUntilSearchingHasFinished(); + // Filter special document + await filterBar.addFilter('agent', 'is', 'Missing/Fields'); + await PageObjects.discover.waitUntilSearchingHasFinished(); + + await retry.try(async () => { + // navigate to the doc view + await dataGrid.clickRowToggle({ rowIndex: 0 }); + + const details = await dataGrid.getDetailsRow(); + await dataGrid.addInclusiveFilter(details, 'referer'); + await PageObjects.discover.waitUntilSearchingHasFinished(); + + const hasInclusiveFilter = await filterBar.hasFilter( + 'referer', + 'exists', + true, + false, + true + ); + expect(hasInclusiveFilter).to.be(true); + + await dataGrid.clickRowToggle({ rowIndex: 0 }); + const detailsExcluding = await dataGrid.getDetailsRow(); + await dataGrid.removeInclusiveFilter(detailsExcluding, 'referer'); + await PageObjects.discover.waitUntilSearchingHasFinished(); + const hasExcludeFilter = await filterBar.hasFilter('referer', 'exists', true, false, false); + expect(hasExcludeFilter).to.be(true); + }); + }); + }); +} diff --git a/test/functional/apps/discover/_data_grid_doc_table.ts b/test/functional/apps/discover/_data_grid_doc_table.ts new file mode 100644 index 0000000000000..1224823abf048 --- /dev/null +++ b/test/functional/apps/discover/_data_grid_doc_table.ts @@ -0,0 +1,132 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const dataGrid = getService('dataGrid'); + const log = getService('log'); + const retry = getService('retry'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const PageObjects = getPageObjects(['common', 'discover', 'header', 'timePicker']); + const defaultSettings = { + defaultIndex: 'logstash-*', + 'doc_table:legacy': false, + }; + + describe('discover data grid doc table', function describeIndexTests() { + const defaultRowsLimit = 25; + + before(async function () { + log.debug('load kibana index with default index pattern'); + await esArchiver.load('discover'); + await esArchiver.loadIfNeeded('logstash_functional'); + await kibanaServer.uiSettings.replace(defaultSettings); + await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); + await PageObjects.common.navigateToApp('discover'); + }); + + it('should show the first 50 rows by default', async function () { + // with the default range the number of hits is ~14000 + const rows = await dataGrid.getDocTableRows(); + expect(rows.length).to.be(defaultRowsLimit); + }); + + it('should refresh the table content when changing time window', async function () { + const initialRows = await dataGrid.getDocTableRows(); + + const fromTime = 'Sep 20, 2015 @ 23:00:00.000'; + const toTime = 'Sep 20, 2015 @ 23:14:00.000'; + + await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.discover.waitUntilSearchingHasFinished(); + + const finalRows = await PageObjects.discover.getDocTableRows(); + expect(finalRows.length).to.be.below(initialRows.length); + await PageObjects.timePicker.setDefaultAbsoluteRange(); + }); + + describe('expand a document row', function () { + const rowToInspect = 1; + + it('should expand the detail row when the toggle arrow is clicked', async function () { + await retry.try(async function () { + await dataGrid.clickRowToggle({ isAnchorRow: false, rowIndex: rowToInspect - 1 }); + const detailsEl = await dataGrid.getDetailsRows(); + const defaultMessageEl = await detailsEl[0].findByTestSubject('docTableRowDetailsTitle'); + expect(defaultMessageEl).to.be.ok(); + await dataGrid.closeFlyout(); + }); + }); + + it('should show the detail panel actions', async function () { + await retry.try(async function () { + await dataGrid.clickRowToggle({ isAnchorRow: false, rowIndex: rowToInspect - 1 }); + const [surroundingActionEl, singleActionEl] = await dataGrid.getRowActions({ + isAnchorRow: false, + rowIndex: rowToInspect - 1, + }); + expect(surroundingActionEl).to.be.ok(); + expect(singleActionEl).to.be.ok(); + await dataGrid.closeFlyout(); + }); + }); + }); + + describe('add and remove columns', function () { + const extraColumns = ['phpmemory', 'ip']; + + afterEach(async function () { + for (const column of extraColumns) { + await PageObjects.discover.clickFieldListItemRemove(column); + await PageObjects.header.waitUntilLoadingHasFinished(); + } + }); + + it('should add more columns to the table', async function () { + for (const column of extraColumns) { + await PageObjects.discover.clearFieldSearchInput(); + await PageObjects.discover.findFieldByName(column); + await PageObjects.discover.clickFieldListItemAdd(column); + await PageObjects.header.waitUntilLoadingHasFinished(); + // test the header now + const header = await dataGrid.getHeaderFields(); + expect(header.join(' ')).to.have.string(column); + } + }); + + it('should remove columns from the table', async function () { + for (const column of extraColumns) { + await PageObjects.discover.clearFieldSearchInput(); + await PageObjects.discover.findFieldByName(column); + await PageObjects.discover.clickFieldListItemAdd(column); + await PageObjects.header.waitUntilLoadingHasFinished(); + } + // remove the second column + await PageObjects.discover.clickFieldListItemAdd(extraColumns[1]); + await PageObjects.header.waitUntilLoadingHasFinished(); + // test that the second column is no longer there + const header = await dataGrid.getHeaderFields(); + expect(header.join(' ')).to.not.have.string(extraColumns[1]); + }); + }); + }); +} diff --git a/test/functional/apps/discover/_data_grid_field_data.ts b/test/functional/apps/discover/_data_grid_field_data.ts new file mode 100644 index 0000000000000..8224f59f7fabf --- /dev/null +++ b/test/functional/apps/discover/_data_grid_field_data.ts @@ -0,0 +1,99 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const retry = getService('retry'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const toasts = getService('toasts'); + const queryBar = getService('queryBar'); + const PageObjects = getPageObjects(['common', 'header', 'discover', 'visualize', 'timePicker']); + const defaultSettings = { defaultIndex: 'logstash-*', 'doc_table:legacy': false }; + const dataGrid = getService('dataGrid'); + + describe('discover data grid field data tests', function describeIndexTests() { + this.tags('includeFirefox'); + before(async function () { + await esArchiver.load('discover'); + await esArchiver.loadIfNeeded('logstash_functional'); + await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); + await kibanaServer.uiSettings.update(defaultSettings); + await PageObjects.common.navigateToApp('discover'); + }); + describe('field data', function () { + it('search php should show the correct hit count', async function () { + const expectedHitCount = '445'; + await retry.try(async function () { + await queryBar.setQuery('php'); + await queryBar.submitQuery(); + const hitCount = await PageObjects.discover.getHitCount(); + expect(hitCount).to.be(expectedHitCount); + }); + }); + + it('the search term should be highlighted in the field data', async function () { + // marks is the style that highlights the text in yellow + const marks = await PageObjects.discover.getMarks(); + expect(marks.length).to.be(25); + expect(marks.indexOf('php')).to.be(0); + }); + + it('search type:apache should show the correct hit count', async function () { + const expectedHitCount = '11,156'; + await queryBar.setQuery('type:apache'); + await queryBar.submitQuery(); + await retry.try(async function tryingForTime() { + const hitCount = await PageObjects.discover.getHitCount(); + expect(hitCount).to.be(expectedHitCount); + }); + }); + + it('doc view should show Time and _source columns', async function () { + const expectedHeader = 'Time (@timestamp) _source'; + const DocHeader = await dataGrid.getHeaderFields(); + expect(DocHeader.join(' ')).to.be(expectedHeader); + }); + + it('doc view should sort ascending', async function () { + const expectedTimeStamp = 'Sep 20, 2015 @ 00:00:00.000'; + await dataGrid.clickDocSortAsc(); + await PageObjects.discover.waitUntilSearchingHasFinished(); + + await retry.try(async function tryingForTime() { + const rowData = await dataGrid.getFields(); + expect(rowData[0][0].startsWith(expectedTimeStamp)).to.be.ok(); + }); + }); + + it('a bad syntax query should show an error message', async function () { + const expectedError = + 'Expected ":", "<", "<=", ">", ">=", AND, OR, end of input, ' + + 'whitespace but "(" found.'; + await queryBar.setQuery('xxx(yyy))'); + await queryBar.submitQuery(); + const { message } = await toasts.getErrorToast(); + expect(message).to.contain(expectedError); + await toasts.dismissToast(); + }); + }); + }); +} diff --git a/test/functional/apps/discover/_discover.ts b/test/functional/apps/discover/_discover.ts index e52c33078029a..1b333c377f777 100644 --- a/test/functional/apps/discover/_discover.ts +++ b/test/functional/apps/discover/_discover.ts @@ -110,7 +110,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); - it('should modify the time range when the histogram is brushed', async function () { + it.skip('should modify the time range when the histogram is brushed', async function () { await PageObjects.timePicker.setDefaultAbsoluteRange(); await PageObjects.discover.brushHistogram(); await PageObjects.discover.waitUntilSearchingHasFinished(); diff --git a/test/functional/apps/discover/_doc_table.ts b/test/functional/apps/discover/_doc_table.ts index 20fda144b338e..40a6ab31f7d4c 100644 --- a/test/functional/apps/discover/_doc_table.ts +++ b/test/functional/apps/discover/_doc_table.ts @@ -131,13 +131,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('should add more columns to the table', async function () { - const [column] = extraColumns; - await PageObjects.discover.findFieldByName(column); - log.debug(`add a ${column} column`); - await PageObjects.discover.clickFieldListItemAdd(column); - await PageObjects.header.waitUntilLoadingHasFinished(); - // test the header now - expect(await PageObjects.discover.getDocHeader()).to.have.string(column); + for (const column of extraColumns) { + await PageObjects.discover.clearFieldSearchInput(); + await PageObjects.discover.findFieldByName(column); + await PageObjects.discover.clickFieldListItemAdd(column); + await PageObjects.header.waitUntilLoadingHasFinished(); + // test the header now + expect(await PageObjects.discover.getDocHeader()).to.have.string(column); + } }); it('should remove columns from the table', async function () { diff --git a/test/functional/apps/discover/index.ts b/test/functional/apps/discover/index.ts index c13529b7d1b43..450049af66abf 100644 --- a/test/functional/apps/discover/index.ts +++ b/test/functional/apps/discover/index.ts @@ -51,5 +51,10 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./_date_nanos')); loadTestFile(require.resolve('./_date_nanos_mixed')); loadTestFile(require.resolve('./_indexpattern_without_timefield')); + loadTestFile(require.resolve('./_data_grid')); + loadTestFile(require.resolve('./_data_grid_context')); + loadTestFile(require.resolve('./_data_grid_field_data')); + loadTestFile(require.resolve('./_data_grid_doc_navigation')); + loadTestFile(require.resolve('./_data_grid_doc_table')); }); } diff --git a/test/functional/config.js b/test/functional/config.js index 5bef9896d17cc..ea6e75b174b4c 100644 --- a/test/functional/config.js +++ b/test/functional/config.js @@ -59,6 +59,7 @@ export default async function ({ readConfigFile }) { defaults: { 'accessibility:disableAnimations': true, 'dateFormat:tz': 'UTC', + 'visualization:visualize:legacyChartsLibrary': true, }, }, diff --git a/test/functional/services/data_grid.ts b/test/functional/services/data_grid.ts index 209e30d23ca3c..c538d8156103c 100644 --- a/test/functional/services/data_grid.ts +++ b/test/functional/services/data_grid.ts @@ -24,10 +24,15 @@ interface TabbedGridData { columns: string[]; rows: string[][]; } +interface SelectOptions { + isAnchorRow?: boolean; + rowIndex: number; +} -export function DataGridProvider({ getService }: FtrProviderContext) { +export function DataGridProvider({ getService, getPageObjects }: FtrProviderContext) { const find = getService('find'); const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['common', 'header']); class DataGrid { async getDataGridTableData(): Promise { @@ -103,6 +108,137 @@ export function DataGridProvider({ getService }: FtrProviderContext) { [data-test-subj="dataGridRowCell"]:nth-of-type(${columnIndex})` ); } + public async getFields() { + const rows = await find.allByCssSelector('.euiDataGridRow'); + + const result = []; + for (const row of rows) { + const cells = await row.findAllByClassName('euiDataGridRowCell__truncate'); + const cellsText = []; + let cellIdx = 0; + for (const cell of cells) { + if (cellIdx > 0) { + cellsText.push(await cell.getVisibleText()); + } + cellIdx++; + } + result.push(cellsText); + } + return result; + } + + public async getTable(selector: string = 'docTable') { + return await testSubjects.find(selector); + } + + public async getBodyRows(): Promise { + const table = await this.getTable(); + return await table.findAllByTestSubject('dataGridRow'); + } + + public async getDocTableRows() { + const table = await this.getTable(); + return await table.findAllByTestSubject('dataGridRow'); + } + + public async getAnchorRow(): Promise { + const table = await this.getTable(); + return await table.findByTestSubject('~docTableAnchorRow'); + } + + public async getRow(options: SelectOptions): Promise { + return options.isAnchorRow + ? await this.getAnchorRow() + : (await this.getBodyRows())[options.rowIndex]; + } + + public async clickRowToggle( + options: SelectOptions = { isAnchorRow: false, rowIndex: 0 } + ): Promise { + const row = await this.getRow(options); + const toggle = await row.findByTestSubject('~docTableExpandToggleColumn'); + await toggle.click(); + } + + public async getDetailsRows(): Promise { + return await testSubjects.findAll('docTableDetailsFlyout'); + } + + public async closeFlyout() { + await testSubjects.click('euiFlyoutCloseButton'); + } + + public async getHeaderFields(): Promise { + const result = await find.allByCssSelector('.euiDataGridHeaderCell__content'); + const textArr = []; + let idx = 0; + for (const cell of result) { + if (idx > 0) { + textArr.push(await cell.getVisibleText()); + } + idx++; + } + return Promise.resolve(textArr); + } + + public async getRowActions( + options: SelectOptions = { isAnchorRow: false, rowIndex: 0 } + ): Promise { + const detailsRow = (await this.getDetailsRows())[options.rowIndex]; + return await detailsRow.findAllByTestSubject('~docTableRowAction'); + } + + public async clickDocSortAsc() { + await find.clickByCssSelector('.euiDataGridHeaderCell__button'); + await find.clickByButtonText('Sort New-Old'); + } + + public async clickDocSortDesc() { + await find.clickByCssSelector('.euiDataGridHeaderCell__button'); + await find.clickByButtonText('Sort Old-New'); + } + public async getDetailsRow(): Promise { + const detailRows = await this.getDetailsRows(); + return detailRows[0]; + } + public async addInclusiveFilter( + detailsRow: WebElementWrapper, + fieldName: string + ): Promise { + const tableDocViewRow = await this.getTableDocViewRow(detailsRow, fieldName); + const addInclusiveFilterButton = await this.getAddInclusiveFilterButton(tableDocViewRow); + await addInclusiveFilterButton.click(); + await PageObjects.header.awaitGlobalLoadingIndicatorHidden(); + } + + public async getAddInclusiveFilterButton( + tableDocViewRow: WebElementWrapper + ): Promise { + return await tableDocViewRow.findByTestSubject(`~addInclusiveFilterButton`); + } + + public async getTableDocViewRow( + detailsRow: WebElementWrapper, + fieldName: string + ): Promise { + return await detailsRow.findByTestSubject(`~tableDocViewRow-${fieldName}`); + } + + public async getRemoveInclusiveFilterButton( + tableDocViewRow: WebElementWrapper + ): Promise { + return await tableDocViewRow.findByTestSubject(`~removeInclusiveFilterButton`); + } + + public async removeInclusiveFilter( + detailsRow: WebElementWrapper, + fieldName: string + ): Promise { + const tableDocViewRow = await this.getTableDocViewRow(detailsRow, fieldName); + const addInclusiveFilterButton = await this.getRemoveInclusiveFilterButton(tableDocViewRow); + await addInclusiveFilterButton.click(); + await PageObjects.header.awaitGlobalLoadingIndicatorHidden(); + } } return new DataGrid(); diff --git a/test/scripts/jenkins_build_kibana.sh b/test/scripts/jenkins_build_kibana.sh index f449986713f97..6184708ea3fc6 100755 --- a/test/scripts/jenkins_build_kibana.sh +++ b/test/scripts/jenkins_build_kibana.sh @@ -10,7 +10,7 @@ fi export KBN_NP_PLUGINS_BUILT=true echo " -> Ensuring all functional tests are in a ciGroup" -yarn run grunt functionalTests:ensureAllTestsInCiGroup; +node scripts/ensure_all_tests_in_ci_group; # Do not build kibana for code coverage run if [[ -z "$CODE_COVERAGE" ]] ; then diff --git a/test/scripts/jenkins_docs.sh b/test/scripts/jenkins_docs.sh deleted file mode 100755 index f447afda1f948..0000000000000 --- a/test/scripts/jenkins_docs.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -set -e -source "$(dirname $0)/../../src/dev/ci_setup/setup.sh" - -"$(FORCE_COLOR=0 yarn bin)/grunt" docker:docs; diff --git a/test/scripts/test/xpack_karma.sh b/test/scripts/test/xpack_karma.sh deleted file mode 100755 index 9078f01f1b870..0000000000000 --- a/test/scripts/test/xpack_karma.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -source src/dev/ci_setup/setup_env.sh - -cd x-pack -checks-reporter-with-killswitch "X-Pack Karma Tests" yarn test:karma diff --git a/vars/tasks.groovy b/vars/tasks.groovy index 221e93fd7b839..18be6e69a2637 100644 --- a/vars/tasks.groovy +++ b/vars/tasks.groovy @@ -115,14 +115,16 @@ def functionalXpack(Map params = [:]) { task(kibanaPipeline.functionalTestProcess('xpack-savedObjectsFieldMetrics', './test/scripts/jenkins_xpack_saved_objects_field_metrics.sh')) } - whenChanged([ - 'x-pack/plugins/security_solution/', - 'x-pack/test/security_solution_cypress/', - 'x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/', - 'x-pack/plugins/triggers_actions_ui/public/application/context/actions_connectors_context.tsx', - ]) { - task(kibanaPipeline.functionalTestProcess('xpack-securitySolutionCypress', './test/scripts/jenkins_security_solution_cypress.sh')) - } + whenChanged([ + 'x-pack/plugins/security_solution/', + 'x-pack/test/security_solution_cypress/', + 'x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/', + 'x-pack/plugins/triggers_actions_ui/public/application/context/actions_connectors_context.tsx', + ]) { + if (githubPr.isPr()) { + task(kibanaPipeline.functionalTestProcess('xpack-securitySolutionCypress', './test/scripts/jenkins_security_solution_cypress.sh')) + } + } } } diff --git a/x-pack/build_chromium/README.md b/x-pack/build_chromium/README.md index ce7e110a5f914..51c034e510024 100644 --- a/x-pack/build_chromium/README.md +++ b/x-pack/build_chromium/README.md @@ -1,13 +1,15 @@ # Chromium build -We ship our own headless build of Chromium which is significantly smaller than the standard binaries shipped by Google. The scripts in this folder can be used to initialize the build environments and run the build on Mac, Windows, and Linux. +We ship our own headless build of Chromium which is significantly smaller than +the standard binaries shipped by Google. The scripts in this folder can be used +to accept a commit hash from the Chromium repository, and initialize the build +environments and run the build on Mac, Windows, and Linux. -The official Chromium build process is poorly documented, and seems to have breaking changes fairly regularly. The build pre-requisites, and the build flags change over time, so it is likely that the scripts in this directory will be out of date by the time we have to do another Chromium build. - -This document is an attempt to note all of the gotchas we've come across while building, so that the next time we have to tinker here, we'll have a good starting point. - -# Before you begin -You'll need access to our GCP account, which is where we have two machines provisioned for the Linux and Windows builds. Mac builds can be achieved locally, and are a great place to start to gain familiarity. +## Before you begin +If you wish to use a remote VM to build, you'll need access to our GCP account, +which is where we have two machines provisioned for the Linux and Windows +builds. Mac builds can be achieved locally, and are a great place to start to +gain familiarity. 1. Login to our GCP instance [here using your okta credentials](https://console.cloud.google.com/). 2. Click the "Compute Engine" tab. @@ -15,21 +17,89 @@ You'll need access to our GCP account, which is where we have two machines provi 4. If #3 fails, you'll have to spin up new instances. Generally, these need `n1-standard-8` types or 8 vCPUs/30 GB memory. 5. Ensure that there's enough room left on the disk. `ncdu` is a good linux util to verify what's claming space. +## Usage + +``` +# Create a dedicated working directory for this directory of Python scripts. +mkdir ~/chromium && cd ~/chromium +# Copy the scripts from the Kibana repo to use them conveniently in the working directory +cp -r ~/path/to/kibana/x-pack/build_chromium . +# Install the OS packages, configure the environment, download the chromium source +python ./build_chromium/init.sh [arch_name] + +# Run the build script with the path to the chromium src directory, the git commit id +python ./build_chromium/build.py + +# You can add an architecture flag for ARM +python ./build_chromium/build.py arm64 +``` + +## Getting the Commit ID +Getting `` can be tricky. The best technique seems to be: +1. Create a temporary working directory and intialize yarn +2. `yarn add puppeteer # install latest puppeter` +3. Look through puppeteer's node module files to find the "chromium revision" (a custom versioning convention for Chromium). +4. Use `https://crrev.com` and look up the revision and find the git commit info. + +The official Chromium build process is poorly documented, and seems to have +breaking changes fairly regularly. The build pre-requisites, and the build +flags change over time, so it is likely that the scripts in this directory will +be out of date by the time we have to do another Chromium build. + +This document is an attempt to note all of the gotchas we've come across while +building, so that the next time we have to tinker here, we'll have a good +starting point. + ## Build args -Chromium is built via a build tool called "ninja". The build can be configured by specifying build flags either in an "args.gn" file or via commandline args. We have an "args.gn" file per platform: +A good how-to on building Chromium from source is +[here](https://chromium.googlesource.com/chromium/src/+/master/docs/get_the_code.md). + +There are documents for each OS that will explain how to customize arguments +for the build using the `gn` tool. Those instructions do not apply for the +Kibana Chromium build. Our `build.py` script ensure the correct `args.gn` +file gets used for build arguments. -- mac: darwin/args.gn -- linux 64bit: linux-x64/args.gn +We have an `args.gn` file per platform: + +- mac: `darwin/args.gn` +- linux 64bit: `linux-x64/args.gn` +- windows: `windows/args.gn` - ARM 64bit: linux-aarch64/args.gn -- windows: windows/args.gn -The various build flags are not well documented. Some are documented [here](https://www.chromium.org/developers/gn-build-configuration). Some, such as `enable_basic_printing = false`, I only found by poking through 3rd party build scripts. +To get a list of the build arguments that are enabled, install `depot_tools` and run +`gn args out/headless --list`. It prints out all of the flags and their +settings, including the defaults. + +The various build flags are not well documented. Some are documented +[here](https://www.chromium.org/developers/gn-build-configuration). -As of this writing, there is an officially supported headless Chromium build args file for Linux: `build/args/headless.gn`. This does not work on Windows or Mac, so we have taken that as our starting point, and modified it until the Windows / Mac builds succeeded. +As of this writing, there is an officially supported headless Chromium build +args file for Linux: `build/args/headless.gn`. This does not work on Windows or +Mac, so we have taken that as our starting point, and modified it until the +Windows / Mac builds succeeded. **NOTE:** Please, make sure you consult @elastic/kibana-security before you change, remove or add any of the build flags. +## Building locally + +You can skip the step of running `/init.sh` for your OS if you already +have your environment set up, and the chromium source cloned. + +To get the Chromium code, refer to the [documentation](https://chromium.googlesource.com/chromium/src/+/master/docs/get_the_code.md). +Install `depot_tools` as suggested, since it comes with useful scripts. Use the +`fetch` command to clone the chromium repository. To set up and run the build, +use the Kibana `build.py` script (in this directory). + +It's recommended that you create a working directory for the chromium source +code and all the build tools, and run the commands from there: +``` +mkdir ~/chromium && cd ~/chromium +cp -r ~/path/to/kibana/x-pack/build_chromium . +python ./build_chromium/init.sh [arch_name] +python ./build_chromium/build.py +``` + ## VMs I ran Linux and Windows VMs in GCP with the following specs: @@ -57,7 +127,8 @@ The more cores the better, as the build makes effective use of each. For Linux, ## Initializing each VM / environment -You only need to initialize each environment once. NOTE: on Mac OS you'll need to install XCode and accept the license agreement. +In a VM, you'll want to use the init scripts to to initialize each environment. +On Mac OS you'll need to install XCode and accept the license agreement. Create the build folder: @@ -86,16 +157,6 @@ In windows, at least, you will need to do a number of extra steps: ## Building -Find the sha of the Chromium commit you wish to build. Most likely, you want to build the Chromium revision that is tied to the version of puppeteer that we're using. - -Find the Chromium revision (run in kibana's working directory): - -- `cat node_modules/puppeteer-core/package.json | grep chromium_revision` -- Take the revision number from that, and tack it to the end of this URL: https://crrev.com - - (For example, puppeteer@1.19.0 has rev (674921): https://crrev.com/674921) -- Grab the SHA from there - - (For example, rev 674921 has sha 312d84c8ce62810976feda0d3457108a6dfff9e6) - Note: In Linux, you should run the build command in tmux so that if your ssh session disconnects, the build can keep going. To do this, just type `tmux` into your terminal to hop into a tmux session. If you get disconnected, you can hop back in like so: - SSH into the server diff --git a/x-pack/build_chromium/build.py b/x-pack/build_chromium/build.py index 52ba325d6f726..8622f4a9d4c0b 100644 --- a/x-pack/build_chromium/build.py +++ b/x-pack/build_chromium/build.py @@ -1,55 +1,80 @@ -import subprocess, os, sys, platform, zipfile, hashlib, shutil -from build_util import runcmd, mkdir, md5_file, script_dir, root_dir, configure_environment +import os, subprocess, sys, platform, zipfile, hashlib, shutil +from os import path +from build_util import ( + runcmd, + runcmdsilent, + mkdir, + md5_file, + configure_environment, +) # This file builds Chromium headless on Windows, Mac, and Linux. # Verify that we have an argument, and if not print instructions if (len(sys.argv) < 2): print('Usage:') - print('python build.py {chromium_version}') + print('python build.py {chromium_version} [arch_name]') print('Example:') print('python build.py 68.0.3440.106') print('python build.py 4747cc23ae334a57a35ed3c8e6adcdbc8a50d479') + print('python build.py 4747cc23ae334a57a35ed3c8e6adcdbc8a50d479 arm64 # build for ARM architecture') + print sys.exit(1) +src_path = path.abspath(path.join(os.curdir, 'chromium', 'src')) +build_path = path.abspath(path.join(src_path, '..', '..')) +build_chromium_path = path.abspath(path.dirname(__file__)) +argsgn_file = path.join(build_chromium_path, platform.system().lower(), 'args.gn') + # The version of Chromium we wish to build. This can be any valid git # commit, tag, or branch, so: 68.0.3440.106 or # 4747cc23ae334a57a35ed3c8e6adcdbc8a50d479 source_version = sys.argv[1] +base_version = source_version[:7].strip('.') # Set to "arm" to build for ARM on Linux arch_name = sys.argv[2] if len(sys.argv) >= 3 else 'x64' -print('Building Chromium ' + source_version + ' for ' + arch_name) - -# Set the environment variables required by the build tools -print('Configuring the build environment') -configure_environment() - -# Sync the codebase to the correct version, syncing master first -# to ensure that we actually have all the versions we may refer to -print('Syncing source code') - -os.chdir(os.path.join(root_dir, 'chromium/src')) - -runcmd('git checkout master') -runcmd('git fetch origin') -runcmd('gclient sync --with_branch_heads --with_tags --jobs 16') -runcmd('git checkout ' + source_version) -runcmd('gclient sync --with_branch_heads --with_tags --jobs 16') -runcmd('gclient runhooks') +if arch_name != 'x64' and arch_name != 'arm64': + raise Exception('Unexpected architecture: ' + arch_name) + +print('Building Chromium ' + source_version + ' for ' + arch_name + ' from ' + src_path) +print('src path: ' + src_path) +print('depot_tools path: ' + path.join(build_path, 'depot_tools')) +print('build_chromium_path: ' + build_chromium_path) +print('args.gn file: ' + argsgn_file) +print + +# Sync the codebase to the correct version +print('Setting local tracking branch') +print(' > cd ' + src_path) +os.chdir(src_path) + +checked_out = runcmdsilent('git checkout build-' + base_version) +if checked_out != 0: + print('Syncing remote version') + runcmd('git fetch origin ' + source_version) + print('Creating a new branch for tracking the source version') + runcmd('git checkout -b build-' + base_version + ' ' + source_version) + +depot_tools_path = os.path.join(build_path, 'depot_tools') +path_value = depot_tools_path + os.pathsep + os.environ['PATH'] +print('Updating PATH for depot_tools: ' + path_value) +os.environ['PATH'] = path_value +print('Updating all modules') +runcmd('gclient sync') # Copy build args/{Linux | Darwin | Windows}.gn from the root of our directory to out/headless/args.gn, -platform_build_args = os.path.join(script_dir, platform.system().lower(), 'args.gn') +argsgn_destination = path.abspath('out/headless/args.gn') print('Generating platform-specific args') -print('Copying build args: ' + platform_build_args + ' to out/headless/args.gn') mkdir('out/headless') -shutil.copyfile(platform_build_args, 'out/headless/args.gn') +print(' > cp ' + argsgn_file + ' ' + argsgn_destination) +shutil.copyfile(argsgn_file, argsgn_destination) print('Adding target_cpu to args') f = open('out/headless/args.gn', 'a') -f.write('\rtarget_cpu = "' + arch_name + '"') +f.write('\rtarget_cpu = "' + arch_name + '"\r') f.close() runcmd('gn gen out/headless') @@ -67,37 +92,38 @@ # Create the zip and generate the md5 hash using filenames like: # chromium-4747cc2-linux_x64.zip -base_filename = 'out/headless/chromium-' + source_version[:7].strip('.') + '-' + platform.system().lower() + '_' + arch_name +base_filename = 'out/headless/chromium-' + base_version + '-' + platform.system().lower() + '_' + arch_name zip_filename = base_filename + '.zip' md5_filename = base_filename + '.md5' -print('Creating ' + zip_filename) +print('Creating ' + path.join(src_path, zip_filename)) archive = zipfile.ZipFile(zip_filename, mode='w', compression=zipfile.ZIP_DEFLATED) def archive_file(name): """A little helper function to write individual files to the zip file""" - from_path = os.path.join('out/headless', name) - to_path = os.path.join('headless_shell-' + platform.system().lower() + '_' + arch_name, name) + from_path = path.join('out/headless', name) + to_path = path.join('headless_shell-' + platform.system().lower() + '_' + arch_name, name) archive.write(from_path, to_path) + return to_path # Each platform has slightly different requirements for what dependencies # must be bundled with the Chromium executable. if platform.system() == 'Linux': archive_file('headless_shell') - archive_file(os.path.join('swiftshader', 'libEGL.so')) - archive_file(os.path.join('swiftshader', 'libGLESv2.so')) + archive_file(path.join('swiftshader', 'libEGL.so')) + archive_file(path.join('swiftshader', 'libGLESv2.so')) if arch_name == 'arm64': - archive_file(os.path.join('swiftshader', 'libEGL.so')) + archive_file(path.join('swiftshader', 'libEGL.so')) elif platform.system() == 'Windows': archive_file('headless_shell.exe') archive_file('dbghelp.dll') archive_file('icudtl.dat') - archive_file(os.path.join('swiftshader', 'libEGL.dll')) - archive_file(os.path.join('swiftshader', 'libEGL.dll.lib')) - archive_file(os.path.join('swiftshader', 'libGLESv2.dll')) - archive_file(os.path.join('swiftshader', 'libGLESv2.dll.lib')) + archive_file(path.join('swiftshader', 'libEGL.dll')) + archive_file(path.join('swiftshader', 'libEGL.dll.lib')) + archive_file(path.join('swiftshader', 'libGLESv2.dll')) + archive_file(path.join('swiftshader', 'libGLESv2.dll.lib')) elif platform.system() == 'Darwin': archive_file('headless_shell') @@ -107,6 +133,6 @@ def archive_file(name): archive.close() -print('Creating ' + md5_filename) +print('Creating ' + path.join(src_path, md5_filename)) with open (md5_filename, 'w') as f: f.write(md5_file(zip_filename)) diff --git a/x-pack/build_chromium/build_util.py b/x-pack/build_chromium/build_util.py index 00ca13d32dba8..eaa94e5170d5c 100644 --- a/x-pack/build_chromium/build_util.py +++ b/x-pack/build_chromium/build_util.py @@ -1,33 +1,45 @@ -import os, hashlib +import os, hashlib, platform, sys # This file contains various utility functions used by the init and build scripts -# Compute the root build and script directory as relative to this file -script_dir = os.path.realpath(os.path.join(__file__, '..')) -root_dir = os.path.realpath(os.path.join(script_dir, '..')) +def runcmdsilent(cmd): + """Executes a string command in the shell""" + print(' > ' + cmd) + return os.system(cmd) def runcmd(cmd): """Executes a string command in the shell""" - print(cmd) + print(' > ' + cmd) result = os.system(cmd) if result != 0: raise Exception(cmd + ' returned ' + str(result)) def mkdir(dir): + print(' > mkdir -p ' + dir) """Makes a directory if it doesn't exist""" if not os.path.exists(dir): - print('mkdir -p ' + dir) return os.makedirs(dir) def md5_file(filename): """Builds a hex md5 hash of the given file""" md5 = hashlib.md5() - with open(filename, 'rb') as f: - for chunk in iter(lambda: f.read(128 * md5.block_size), b''): + with open(filename, 'rb') as f: + for chunk in iter(lambda: f.read(128 * md5.block_size), b''): md5.update(chunk) return md5.hexdigest() -def configure_environment(): - """Configures temporary environment variables required by Chromium's build""" - depot_tools_path = os.path.join(root_dir, 'depot_tools') - os.environ['PATH'] = depot_tools_path + os.pathsep + os.environ['PATH'] +def configure_environment(arch_name, build_path, src_path): + """Runs install scripts for deps, and configures temporary environment variables required by Chromium's build""" + + if platform.system() == 'Linux': + if arch_name: + print('Running sysroot install script...') + sysroot_cmd = src_path + '/build/linux/sysroot_scripts/install-sysroot.py --arch=' + arch_name + runcmd(sysroot_cmd) + print('Running install-build-deps...') + runcmd(src_path + '/build/install-build-deps.sh') + + depot_tools_path = os.path.join(build_path, 'depot_tools') + full_path = depot_tools_path + os.pathsep + os.environ['PATH'] + print('Updating PATH for depot_tools: ' + full_path) + os.environ['PATH'] = full_path diff --git a/x-pack/build_chromium/init.py b/x-pack/build_chromium/init.py index f543922f7653a..c0dd60f1cfcb0 100644 --- a/x-pack/build_chromium/init.py +++ b/x-pack/build_chromium/init.py @@ -1,38 +1,47 @@ import os, platform, sys -from build_util import runcmd, mkdir, md5_file, root_dir, configure_environment +from os import path +from build_util import runcmd, mkdir, md5_file, configure_environment # This is a cross-platform initialization script which should only be run # once per environment, and isn't intended to be run directly. You should # run the appropriate platform init script (e.g. Linux/init.sh) which will # call this once the platform-specific initialization has completed. -os.chdir(root_dir) +# Set to "arm" to build for ARM on Linux +arch_name = sys.argv[1] if len(sys.argv) >= 2 else 'x64' +build_path = path.abspath(os.curdir) +src_path = path.abspath(path.join(build_path, 'chromium', 'src')) + +if arch_name != 'x64' and arch_name != 'arm64': + raise Exception('Unexpected architecture: ' + arch_name) # Configure git +print('Configuring git globals...') runcmd('git config --global core.autocrlf false') runcmd('git config --global core.filemode false') runcmd('git config --global branch.autosetuprebase always') # Grab Chromium's custom build tools, if they aren't already installed # (On Windows, they are installed before this Python script is run) -if not os.path.isdir('depot_tools'): +# Put depot_tools on the path so we can properly run the fetch command +if not path.isdir('depot_tools'): + print('Installing depot_tools...') runcmd('git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git') +else: + print('Updating depot_tools...') + original_dir = os.curdir + os.chdir(path.join(build_path, 'depot_tools')) + runcmd('git checkout master') + runcmd('git pull origin master') + os.chdir(original_dir) -# Put depot_tools on the path so we can properly run the fetch command -configure_environment() +configure_environment(arch_name, build_path, src_path) # Fetch the Chromium source code -mkdir('chromium') -os.chdir('chromium') -runcmd('fetch chromium') - -# Build Linux deps -if platform.system() == 'Linux': - os.chdir('src') - - if len(sys.argv) >= 2: - sysroot_cmd = 'build/linux/sysroot_scripts/install-sysroot.py --arch=' + sys.argv[1] - print('Running `' + sysroot_cmd + '`') - runcmd(sysroot_cmd) - - runcmd('build/install-build-deps.sh') +chromium_dir = path.join(build_path, 'chromium') +if not path.isdir(chromium_dir): + mkdir(chromium_dir) + os.chdir(chromium_dir) + runcmd('fetch chromium') +else: + print('Directory exists: ' + chromium_dir + '. Skipping chromium fetch.') diff --git a/x-pack/examples/alerting_example/common/constants.ts b/x-pack/examples/alerting_example/common/constants.ts index 40cc298db795a..8e4ea4faf014c 100644 --- a/x-pack/examples/alerting_example/common/constants.ts +++ b/x-pack/examples/alerting_example/common/constants.ts @@ -4,11 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ +import { AlertTypeParams } from '../../../plugins/alerts/common'; + export const ALERTING_EXAMPLE_APP_ID = 'AlertingExample'; // always firing export const DEFAULT_INSTANCES_TO_GENERATE = 5; -export interface AlwaysFiringParams { +export interface AlwaysFiringParams extends AlertTypeParams { instances?: number; thresholds?: { small?: number; diff --git a/x-pack/examples/alerting_example/public/components/view_astros_alert.tsx b/x-pack/examples/alerting_example/public/components/view_astros_alert.tsx index e4687c75fa0b7..eb682a86f5ff6 100644 --- a/x-pack/examples/alerting_example/public/components/view_astros_alert.tsx +++ b/x-pack/examples/alerting_example/public/components/view_astros_alert.tsx @@ -23,7 +23,7 @@ import { withRouter, RouteComponentProps } from 'react-router-dom'; import { CoreStart } from 'kibana/public'; import { isEmpty } from 'lodash'; import { Alert, AlertTaskState, BASE_ALERT_API_PATH } from '../../../../plugins/alerts/common'; -import { ALERTING_EXAMPLE_APP_ID } from '../../common/constants'; +import { ALERTING_EXAMPLE_APP_ID, AlwaysFiringParams } from '../../common/constants'; type Props = RouteComponentProps & { http: CoreStart['http']; @@ -34,7 +34,7 @@ function hasCraft(state: any): state is { craft: string } { return state && state.craft; } export const ViewPeopleInSpaceAlertPage = withRouter(({ http, id }: Props) => { - const [alert, setAlert] = useState(null); + const [alert, setAlert] = useState | null>(null); const [alertState, setAlertState] = useState(null); useEffect(() => { diff --git a/x-pack/examples/alerting_example/server/alert_types/astros.ts b/x-pack/examples/alerting_example/server/alert_types/astros.ts index 27a8bfc7a53a3..22c2f25c410cd 100644 --- a/x-pack/examples/alerting_example/server/alert_types/astros.ts +++ b/x-pack/examples/alerting_example/server/alert_types/astros.ts @@ -38,7 +38,11 @@ function getCraftFilter(craft: string) { craft === Craft.OuterSpace ? true : craft === person.craft; } -export const alertType: AlertType = { +export const alertType: AlertType< + { outerSpaceCapacity: number; craft: string; op: string }, + { peopleInSpace: number }, + { craft: string } +> = { id: 'example.people-in-space', name: 'People In Space Right Now', actionGroups: [{ id: 'default', name: 'default' }], diff --git a/x-pack/examples/reporting_example/.eslintrc.js b/x-pack/examples/reporting_example/.eslintrc.js new file mode 100644 index 0000000000000..b267018448ba6 --- /dev/null +++ b/x-pack/examples/reporting_example/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + root: true, + extends: ['@elastic/eslint-config-kibana', 'plugin:@elastic/eui/recommended'], + rules: { + '@kbn/eslint/require-license-header': 'off', + }, +}; diff --git a/x-pack/examples/reporting_example/README.md b/x-pack/examples/reporting_example/README.md new file mode 100755 index 0000000000000..186a3fa37f93b --- /dev/null +++ b/x-pack/examples/reporting_example/README.md @@ -0,0 +1,33 @@ +# Example Reporting integration! + +Use this example code to understand how to add a "Generate Report" button to a +Kibana page. This simple example shows that the end-to-end functionality of +generating a screenshot report of a page just requires you to render a React +component that you import from the Reportinng plugin. + +A "reportable" Kibana page is one that has an **alternate version to show the data in a "screenshot-friendly" way**. The alternate version can be reached at a variation of the page's URL that the App team builds. + +A "screenshot-friendly" page has **all interactive features turned off**. These are typically notifications, popups, tooltips, controls, autocomplete libraries, etc. + +Turning off these features **keeps glitches out of the screenshot**, and makes the server-side headless browser **run faster and use less RAM**. + +The URL that Reporting captures is controlled by the application, is a part of +a "jobParams" object that gets passed to the React component imported from +Reporting. The job params give the app control over the end-resulting report: + +- Layout + - Page dimensions + - DOM attributes to select where the visualization container(s) is/are. The App team must add the attributes to DOM elements in their app. + - DOM events that the page fires off and signals when the rendering is done. The App team must implement triggering the DOM events around rendering the data in their app. +- Export type definition + - Processes the jobParams into output data, which is stored in Elasticsearch in the Reporting system index. + - Export type definitions are registered with the Reporting plugin at setup time. + +The existing export type definitions are PDF, PNG, and CSV. They should be +enough for nearly any use case. + +If the existing options are too limited for a future use case, the AppServices +team can assist the App team to implement a custom export type definition of +their own, and register it using the Reporting plugin API **(documentation coming soon)**. + +--- diff --git a/x-pack/examples/reporting_example/common/index.ts b/x-pack/examples/reporting_example/common/index.ts new file mode 100644 index 0000000000000..e47604bd7b823 --- /dev/null +++ b/x-pack/examples/reporting_example/common/index.ts @@ -0,0 +1,2 @@ +export const PLUGIN_ID = 'reportingExample'; +export const PLUGIN_NAME = 'reportingExample'; diff --git a/x-pack/examples/reporting_example/kibana.json b/x-pack/examples/reporting_example/kibana.json new file mode 100644 index 0000000000000..22768338aec37 --- /dev/null +++ b/x-pack/examples/reporting_example/kibana.json @@ -0,0 +1,9 @@ +{ + "id": "reportingExample", + "version": "1.0.0", + "kibanaVersion": "kibana", + "server": false, + "ui": true, + "optionalPlugins": [], + "requiredPlugins": ["reporting", "developerExamples", "navigation"] +} diff --git a/x-pack/examples/reporting_example/public/application.tsx b/x-pack/examples/reporting_example/public/application.tsx new file mode 100644 index 0000000000000..1bb944faad3ea --- /dev/null +++ b/x-pack/examples/reporting_example/public/application.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { AppMountParameters, CoreStart } from '../../../../src/core/public'; +import { StartDeps } from './types'; +import { ReportingExampleApp } from './components/app'; + +export const renderApp = ( + coreStart: CoreStart, + startDeps: StartDeps, + { appBasePath, element }: AppMountParameters +) => { + ReactDOM.render( + , + element + ); + + return () => ReactDOM.unmountComponentAtNode(element); +}; diff --git a/x-pack/examples/reporting_example/public/components/app.tsx b/x-pack/examples/reporting_example/public/components/app.tsx new file mode 100644 index 0000000000000..8f7176675f2c2 --- /dev/null +++ b/x-pack/examples/reporting_example/public/components/app.tsx @@ -0,0 +1,130 @@ +import { + EuiCard, + EuiCode, + EuiFlexGroup, + EuiFlexItem, + EuiHorizontalRule, + EuiIcon, + EuiPage, + EuiPageBody, + EuiPageContent, + EuiPageContentBody, + EuiPageHeader, + EuiPanel, + EuiText, + EuiTitle, +} from '@elastic/eui'; +import { I18nProvider } from '@kbn/i18n/react'; +import React, { useEffect, useState } from 'react'; +import { BrowserRouter as Router } from 'react-router-dom'; +import * as Rx from 'rxjs'; +import { takeWhile } from 'rxjs/operators'; +import { CoreStart } from '../../../../../src/core/public'; +import { NavigationPublicPluginStart } from '../../../../../src/plugins/navigation/public'; +import { constants, ReportingStart } from '../../../../../x-pack/plugins/reporting/public'; +import { JobParamsPDF } from '../../../../plugins/reporting/server/export_types/printable_pdf/types'; + +interface ReportingExampleAppDeps { + basename: string; + notifications: CoreStart['notifications']; + http: CoreStart['http']; + navigation: NavigationPublicPluginStart; + reporting: ReportingStart; +} + +const sourceLogos = ['Beats', 'Cloud', 'Logging', 'Kibana']; + +export const ReportingExampleApp = ({ + basename, + notifications, + http, + reporting, +}: ReportingExampleAppDeps) => { + const { getDefaultLayoutSelectors, ReportingAPIClient } = reporting; + const [logos, setLogos] = useState([]); + + useEffect(() => { + Rx.timer(2200) + .pipe(takeWhile(() => logos.length < sourceLogos.length)) + .subscribe(() => { + setLogos([...sourceLogos.slice(0, logos.length + 1)]); + }); + }); + + const getPDFJobParams = (): JobParamsPDF => { + return { + layout: { + id: constants.LAYOUT_TYPES.PRESERVE_LAYOUT, + selectors: getDefaultLayoutSelectors(), + }, + relativeUrls: ['/app/reportingExample#/intended-visualization'], + objectType: 'develeloperExample', + title: 'Reporting Developer Example', + }; + }; + + // Render the application DOM. + return ( + + + + + + +

Reporting Example

+
+
+ + + +

+ Use the ReportingStart.components.ScreenCapturePanel{' '} + component to add the Reporting panel to your page. +

+ + + + + + + + + + + + + +

+ The logos below are in a data-shared-items-container element + for Reporting. +

+ +
+ + {logos.map((item, index) => ( + + } + title={`Elastic ${item}`} + description="Example of a card's description. Stick to one or two sentences." + onClick={() => {}} + /> + + ))} + +
+
+
+
+
+
+
+
+ ); +}; diff --git a/x-pack/examples/reporting_example/public/index.ts b/x-pack/examples/reporting_example/public/index.ts new file mode 100644 index 0000000000000..a490cf96895be --- /dev/null +++ b/x-pack/examples/reporting_example/public/index.ts @@ -0,0 +1,6 @@ +import { ReportingExamplePlugin } from './plugin'; + +export function plugin() { + return new ReportingExamplePlugin(); +} +export { PluginSetup, PluginStart } from './types'; diff --git a/x-pack/examples/reporting_example/public/plugin.ts b/x-pack/examples/reporting_example/public/plugin.ts new file mode 100644 index 0000000000000..95b4d917f549a --- /dev/null +++ b/x-pack/examples/reporting_example/public/plugin.ts @@ -0,0 +1,41 @@ +import { + AppMountParameters, + AppNavLinkStatus, + CoreSetup, + CoreStart, + Plugin, +} from '../../../../src/core/public'; +import { PLUGIN_ID, PLUGIN_NAME } from '../common'; +import { SetupDeps, StartDeps } from './types'; + +export class ReportingExamplePlugin implements Plugin { + public setup(core: CoreSetup, { developerExamples, ...depsSetup }: SetupDeps): void { + core.application.register({ + id: PLUGIN_ID, + title: PLUGIN_NAME, + navLinkStatus: AppNavLinkStatus.hidden, + async mount(params: AppMountParameters) { + // Load application bundle + const { renderApp } = await import('./application'); + const [coreStart, depsStart] = (await core.getStartServices()) as [ + CoreStart, + StartDeps, + unknown + ]; + // Render the application + return renderApp(coreStart, { ...depsSetup, ...depsStart }, params); + }, + }); + + // Show the app in Developer Examples + developerExamples.register({ + appId: 'reportingExample', + title: 'Reporting integration', + description: 'Demonstrate how to put an Export button on a page and generate reports.', + }); + } + + public start() {} + + public stop() {} +} diff --git a/x-pack/examples/reporting_example/public/types.ts b/x-pack/examples/reporting_example/public/types.ts new file mode 100644 index 0000000000000..d574053266fae --- /dev/null +++ b/x-pack/examples/reporting_example/public/types.ts @@ -0,0 +1,16 @@ +import { DeveloperExamplesSetup } from '../../../../examples/developer_examples/public'; +import { NavigationPublicPluginStart } from '../../../../src/plugins/navigation/public'; +import { ReportingStart } from '../../../plugins/reporting/public'; + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface PluginSetup {} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface PluginStart {} + +export interface SetupDeps { + developerExamples: DeveloperExamplesSetup; +} +export interface StartDeps { + navigation: NavigationPublicPluginStart; + reporting: ReportingStart; +} diff --git a/x-pack/examples/reporting_example/tsconfig.json b/x-pack/examples/reporting_example/tsconfig.json new file mode 100644 index 0000000000000..ef727b3368b12 --- /dev/null +++ b/x-pack/examples/reporting_example/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./target" + }, + "include": [ + "index.ts", + "public/**/*.ts", + "public/**/*.tsx", + "server/**/*.ts", + "common/**/*.ts", + "../../../typings/**/*", + ], + "exclude": [], + "references": [ + { "path": "../../../src/core/tsconfig.json" } + ] +} + diff --git a/x-pack/plugins/actions/server/types.ts b/x-pack/plugins/actions/server/types.ts index b311a602212c7..81d6c3550a53c 100644 --- a/x-pack/plugins/actions/server/types.ts +++ b/x-pack/plugins/actions/server/types.ts @@ -31,6 +31,9 @@ export type ActionTypeSecrets = Record; export type ActionTypeParams = Record; export interface Services { + /** + * @deprecated Use `scopedClusterClient` instead. + */ callCluster: ILegacyScopedClusterClient['callAsCurrentUser']; savedObjectsClient: SavedObjectsClientContract; scopedClusterClient: ElasticsearchClient; diff --git a/x-pack/plugins/alerts/common/alert.ts b/x-pack/plugins/alerts/common/alert.ts index d74f66898eff6..ed3fbcf2ddc9b 100644 --- a/x-pack/plugins/alerts/common/alert.ts +++ b/x-pack/plugins/alerts/common/alert.ts @@ -7,10 +7,8 @@ import { SavedObjectAttribute, SavedObjectAttributes } from 'kibana/server'; import { AlertNotifyWhenType } from './alert_notify_when_type'; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export type AlertTypeState = Record; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export type AlertTypeParams = Record; +export type AlertTypeState = Record; +export type AlertTypeParams = Record; export interface IntervalSchedule extends SavedObjectAttributes { interval: string; @@ -52,7 +50,7 @@ export interface AlertAggregations { alertExecutionStatus: { [status: string]: number }; } -export interface Alert { +export interface Alert { id: string; enabled: boolean; name: string; @@ -61,7 +59,7 @@ export interface Alert { consumer: string; schedule: IntervalSchedule; actions: AlertAction[]; - params: AlertTypeParams; + params: Params; scheduledTaskId?: string; createdBy: string | null; updatedBy: string | null; @@ -76,7 +74,7 @@ export interface Alert { executionStatus: AlertExecutionStatus; } -export type SanitizedAlert = Omit; +export type SanitizedAlert = Omit, 'apiKey'>; export enum HealthStatus { OK = 'ok', diff --git a/x-pack/plugins/alerts/server/alert_instance/create_alert_instance_factory.ts b/x-pack/plugins/alerts/server/alert_instance/create_alert_instance_factory.ts index 0b29262ddcc07..47f013a5d0e55 100644 --- a/x-pack/plugins/alerts/server/alert_instance/create_alert_instance_factory.ts +++ b/x-pack/plugins/alerts/server/alert_instance/create_alert_instance_factory.ts @@ -4,12 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ +import { AlertInstanceContext, AlertInstanceState } from '../types'; import { AlertInstance } from './alert_instance'; -export function createAlertInstanceFactory(alertInstances: Record) { - return (id: string): AlertInstance => { +export function createAlertInstanceFactory< + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +>(alertInstances: Record>) { + return (id: string): AlertInstance => { if (!alertInstances[id]) { - alertInstances[id] = new AlertInstance(); + alertInstances[id] = new AlertInstance(); } return alertInstances[id]; diff --git a/x-pack/plugins/alerts/server/alert_type_registry.ts b/x-pack/plugins/alerts/server/alert_type_registry.ts index d436d1987c027..5e4188c1f3bc1 100644 --- a/x-pack/plugins/alerts/server/alert_type_registry.ts +++ b/x-pack/plugins/alerts/server/alert_type_registry.ts @@ -32,7 +32,7 @@ export interface ConstructorOptions { export interface RegistryAlertType extends Pick< - NormalizedAlertType, + UntypedNormalizedAlertType, | 'name' | 'actionGroups' | 'recoveryActionGroup' @@ -66,16 +66,23 @@ const alertIdSchema = schema.string({ }); export type NormalizedAlertType< - Params extends AlertTypeParams = AlertTypeParams, - State extends AlertTypeState = AlertTypeState, - InstanceState extends AlertInstanceState = AlertInstanceState, - InstanceContext extends AlertInstanceContext = AlertInstanceContext + Params extends AlertTypeParams, + State extends AlertTypeState, + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext > = Omit, 'recoveryActionGroup'> & Pick>, 'recoveryActionGroup'>; +export type UntypedNormalizedAlertType = NormalizedAlertType< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext +>; + export class AlertTypeRegistry { private readonly taskManager: TaskManagerSetupContract; - private readonly alertTypes: Map = new Map(); + private readonly alertTypes: Map = new Map(); private readonly taskRunnerFactory: TaskRunnerFactory; private readonly licenseState: ILicenseState; private readonly licensing: LicensingPluginSetup; @@ -96,10 +103,10 @@ export class AlertTypeRegistry { } public register< - Params extends AlertTypeParams = AlertTypeParams, - State extends AlertTypeState = AlertTypeState, - InstanceState extends AlertInstanceState = AlertInstanceState, - InstanceContext extends AlertInstanceContext = AlertInstanceContext + Params extends AlertTypeParams, + State extends AlertTypeState, + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext >(alertType: AlertType) { if (this.has(alertType.id)) { throw new Error( @@ -113,14 +120,22 @@ export class AlertTypeRegistry { } alertType.actionVariables = normalizedActionVariables(alertType.actionVariables); - const normalizedAlertType = augmentActionGroupsWithReserved(alertType as AlertType); + const normalizedAlertType = augmentActionGroupsWithReserved< + Params, + State, + InstanceState, + InstanceContext + >(alertType); - this.alertTypes.set(alertIdSchema.validate(alertType.id), normalizedAlertType); + this.alertTypes.set( + alertIdSchema.validate(alertType.id), + normalizedAlertType as UntypedNormalizedAlertType + ); this.taskManager.registerTaskDefinitions({ [`alerting:${alertType.id}`]: { title: alertType.name, createTaskRunner: (context: RunContext) => - this.taskRunnerFactory.create(normalizedAlertType, context), + this.taskRunnerFactory.create(normalizedAlertType as UntypedNormalizedAlertType, context), }, }); // No need to notify usage on basic alert types @@ -170,7 +185,7 @@ export class AlertTypeRegistry { producer, minimumLicenseRequired, }, - ]: [string, NormalizedAlertType]) => ({ + ]: [string, UntypedNormalizedAlertType]) => ({ id, name, actionGroups, diff --git a/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts b/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts index f21cd2b02943a..e21fee4ce3d61 100644 --- a/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts +++ b/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts @@ -23,13 +23,13 @@ import { RawAlert, AlertTypeRegistry, AlertAction, - AlertType, IntervalSchedule, SanitizedAlert, AlertTaskState, AlertInstanceSummary, AlertExecutionStatusValues, AlertNotifyWhenType, + AlertTypeParams, } from '../types'; import { validateAlertTypeParams, @@ -44,7 +44,7 @@ import { EncryptedSavedObjectsClient } from '../../../encrypted_saved_objects/se import { TaskManagerStartContract } from '../../../task_manager/server'; import { taskInstanceToAlertTaskInstance } from '../task_runner/alert_task_instance'; import { deleteTaskIfItExists } from '../lib/delete_task_if_it_exists'; -import { RegistryAlertType } from '../alert_type_registry'; +import { RegistryAlertType, UntypedNormalizedAlertType } from '../alert_type_registry'; import { AlertsAuthorization, WriteOperations, ReadOperations } from '../authorization'; import { IEventLogClient } from '../../../../plugins/event_log/server'; import { parseIsoOrRelativeDate } from '../lib/iso_or_relative_date'; @@ -127,16 +127,16 @@ interface AggregateResult { alertExecutionStatus: { [status: string]: number }; } -export interface FindResult { +export interface FindResult { page: number; perPage: number; total: number; - data: SanitizedAlert[]; + data: Array>; } -export interface CreateOptions { +export interface CreateOptions { data: Omit< - Alert, + Alert, | 'id' | 'createdBy' | 'updatedBy' @@ -154,14 +154,14 @@ export interface CreateOptions { }; } -interface UpdateOptions { +interface UpdateOptions { id: string; data: { name: string; tags: string[]; schedule: IntervalSchedule; actions: NormalizedAlertAction[]; - params: Record; + params: Params; throttle: string | null; notifyWhen: AlertNotifyWhenType | null; }; @@ -223,7 +223,10 @@ export class AlertsClient { this.auditLogger = auditLogger; } - public async create({ data, options }: CreateOptions): Promise { + public async create({ + data, + options, + }: CreateOptions): Promise> { const id = SavedObjectsUtils.generateId(); try { @@ -248,7 +251,10 @@ export class AlertsClient { // Throws an error if alert type isn't registered const alertType = this.alertTypeRegistry.get(data.alertTypeId); - const validatedAlertTypeParams = validateAlertTypeParams(alertType, data.params); + const validatedAlertTypeParams = validateAlertTypeParams( + data.params, + alertType.validate?.params + ); const username = await this.getUserName(); const createdAPIKey = data.enabled @@ -334,10 +340,14 @@ export class AlertsClient { }); createdAlert.attributes.scheduledTaskId = scheduledTask.id; } - return this.getAlertFromRaw(createdAlert.id, createdAlert.attributes, references); + return this.getAlertFromRaw(createdAlert.id, createdAlert.attributes, references); } - public async get({ id }: { id: string }): Promise { + public async get({ + id, + }: { + id: string; + }): Promise> { const result = await this.unsecuredSavedObjectsClient.get('alert', id); try { await this.authorization.ensureAuthorized( @@ -361,7 +371,7 @@ export class AlertsClient { savedObject: { type: 'alert', id }, }) ); - return this.getAlertFromRaw(result.id, result.attributes, result.references); + return this.getAlertFromRaw(result.id, result.attributes, result.references); } public async getAlertState({ id }: { id: string }): Promise { @@ -426,9 +436,9 @@ export class AlertsClient { }); } - public async find({ + public async find({ options: { fields, ...options } = {}, - }: { options?: FindOptions } = {}): Promise { + }: { options?: FindOptions } = {}): Promise> { let authorizationTuple; try { authorizationTuple = await this.authorization.getFindAuthorizationFilter(); @@ -475,7 +485,7 @@ export class AlertsClient { ); throw error; } - return this.getAlertFromRaw( + return this.getAlertFromRaw( id, fields ? (pick(attributes, fields) as RawAlert) : attributes, references @@ -605,15 +615,21 @@ export class AlertsClient { return removeResult; } - public async update({ id, data }: UpdateOptions): Promise { + public async update({ + id, + data, + }: UpdateOptions): Promise> { return await retryIfConflicts( this.logger, `alertsClient.update('${id}')`, - async () => await this.updateWithOCC({ id, data }) + async () => await this.updateWithOCC({ id, data }) ); } - private async updateWithOCC({ id, data }: UpdateOptions): Promise { + private async updateWithOCC({ + id, + data, + }: UpdateOptions): Promise> { let alertSavedObject: SavedObject; try { @@ -658,7 +674,7 @@ export class AlertsClient { this.alertTypeRegistry.ensureAlertTypeEnabled(alertSavedObject.attributes.alertTypeId); - const updateResult = await this.updateAlert({ id, data }, alertSavedObject); + const updateResult = await this.updateAlert({ id, data }, alertSavedObject); await Promise.all([ alertSavedObject.attributes.apiKey @@ -692,14 +708,17 @@ export class AlertsClient { return updateResult; } - private async updateAlert( - { id, data }: UpdateOptions, + private async updateAlert( + { id, data }: UpdateOptions, { attributes, version }: SavedObject - ): Promise { + ): Promise> { const alertType = this.alertTypeRegistry.get(attributes.alertTypeId); // Validate - const validatedAlertTypeParams = validateAlertTypeParams(alertType, data.params); + const validatedAlertTypeParams = validateAlertTypeParams( + data.params, + alertType.validate?.params + ); this.validateActions(alertType, data.actions); const { actions, references } = await this.denormalizeActions(data.actions); @@ -1343,7 +1362,7 @@ export class AlertsClient { }) as Alert['actions']; } - private getAlertFromRaw( + private getAlertFromRaw( id: string, rawAlert: RawAlert, references: SavedObjectReference[] | undefined @@ -1351,14 +1370,14 @@ export class AlertsClient { // In order to support the partial update API of Saved Objects we have to support // partial updates of an Alert, but when we receive an actual RawAlert, it is safe // to cast the result to an Alert - return this.getPartialAlertFromRaw(id, rawAlert, references) as Alert; + return this.getPartialAlertFromRaw(id, rawAlert, references) as Alert; } - private getPartialAlertFromRaw( + private getPartialAlertFromRaw( id: string, { createdAt, updatedAt, meta, notifyWhen, scheduledTaskId, ...rawAlert }: Partial, references: SavedObjectReference[] | undefined - ): PartialAlert { + ): PartialAlert { // Not the prettiest code here, but if we want to use most of the // alert fields from the rawAlert using `...rawAlert` kind of access, we // need to specifically delete the executionStatus as it's a different type @@ -1386,7 +1405,10 @@ export class AlertsClient { }; } - private validateActions(alertType: AlertType, actions: NormalizedAlertAction[]): void { + private validateActions( + alertType: UntypedNormalizedAlertType, + actions: NormalizedAlertAction[] + ): void { const { actionGroups: alertTypeActionGroups } = alertType; const usedAlertActionGroups = actions.map((action) => action.group); const availableAlertTypeActionGroups = new Set(map(alertTypeActionGroups, 'id')); diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/create.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/create.test.ts index 5f830a6c5bc51..0424a1295c9b9 100644 --- a/x-pack/plugins/alerts/server/alerts_client/tests/create.test.ts +++ b/x-pack/plugins/alerts/server/alerts_client/tests/create.test.ts @@ -59,7 +59,11 @@ beforeEach(() => { setGlobalDate(); -function getMockData(overwrites: Record = {}): CreateOptions['data'] { +function getMockData( + overwrites: Record = {} +): CreateOptions<{ + bar: boolean; +}>['data'] { return { enabled: true, name: 'abc', @@ -93,7 +97,11 @@ describe('create()', () => { }); describe('authorization', () => { - function tryToExecuteOperation(options: CreateOptions): Promise { + function tryToExecuteOperation( + options: CreateOptions<{ + bar: boolean; + }> + ): Promise { unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ saved_objects: [ { diff --git a/x-pack/plugins/alerts/server/invalidate_pending_api_keys/task.ts b/x-pack/plugins/alerts/server/invalidate_pending_api_keys/task.ts index 91c3f5954d6d0..1c7320d3df6f3 100644 --- a/x-pack/plugins/alerts/server/invalidate_pending_api_keys/task.ts +++ b/x-pack/plugins/alerts/server/invalidate_pending_api_keys/task.ts @@ -193,6 +193,7 @@ async function invalidateApiKeys( encryptedSavedObjectsClient: EncryptedSavedObjectsClient, securityPluginStart?: SecurityPluginStart ) { + // TODO: This could probably send a single request to ES now that the invalidate API supports multiple ids in a single request let totalInvalidated = 0; await Promise.all( apiKeysToInvalidate.saved_objects.map(async (apiKeyObj) => { diff --git a/x-pack/plugins/alerts/server/lib/alert_instance_summary_from_event_log.test.ts b/x-pack/plugins/alerts/server/lib/alert_instance_summary_from_event_log.test.ts index d6357494546b0..0f91e5d0c24a9 100644 --- a/x-pack/plugins/alerts/server/lib/alert_instance_summary_from_event_log.test.ts +++ b/x-pack/plugins/alerts/server/lib/alert_instance_summary_from_event_log.test.ts @@ -635,11 +635,11 @@ export class EventsFactory { } } -function createAlert(overrides: Partial): SanitizedAlert { +function createAlert(overrides: Partial): SanitizedAlert<{ bar: boolean }> { return { ...BaseAlert, ...overrides }; } -const BaseAlert: SanitizedAlert = { +const BaseAlert: SanitizedAlert<{ bar: boolean }> = { id: 'alert-123', alertTypeId: '123', schedule: { interval: '10s' }, diff --git a/x-pack/plugins/alerts/server/lib/alert_instance_summary_from_event_log.ts b/x-pack/plugins/alerts/server/lib/alert_instance_summary_from_event_log.ts index f540f9a9b884c..a020eecd448a4 100644 --- a/x-pack/plugins/alerts/server/lib/alert_instance_summary_from_event_log.ts +++ b/x-pack/plugins/alerts/server/lib/alert_instance_summary_from_event_log.ts @@ -9,7 +9,7 @@ import { IEvent } from '../../../event_log/server'; import { EVENT_LOG_ACTIONS, EVENT_LOG_PROVIDER, LEGACY_EVENT_LOG_ACTIONS } from '../plugin'; export interface AlertInstanceSummaryFromEventLogParams { - alert: SanitizedAlert; + alert: SanitizedAlert<{ bar: boolean }>; events: IEvent[]; dateStart: string; dateEnd: string; diff --git a/x-pack/plugins/alerts/server/lib/validate_alert_type_params.test.ts b/x-pack/plugins/alerts/server/lib/validate_alert_type_params.test.ts index 2814eaef3e02a..634b6885aa59b 100644 --- a/x-pack/plugins/alerts/server/lib/validate_alert_type_params.test.ts +++ b/x-pack/plugins/alerts/server/lib/validate_alert_type_params.test.ts @@ -8,51 +8,19 @@ import { schema } from '@kbn/config-schema'; import { validateAlertTypeParams } from './validate_alert_type_params'; test('should return passed in params when validation not defined', () => { - const result = validateAlertTypeParams( - { - id: 'my-alert-type', - name: 'My description', - actionGroups: [ - { - id: 'default', - name: 'Default', - }, - ], - defaultActionGroupId: 'default', - minimumLicenseRequired: 'basic', - async executor() {}, - producer: 'alerts', - }, - { - foo: true, - } - ); + const result = validateAlertTypeParams({ + foo: true, + }); expect(result).toEqual({ foo: true }); }); test('should validate and apply defaults when params is valid', () => { const result = validateAlertTypeParams( - { - id: 'my-alert-type', - name: 'My description', - actionGroups: [ - { - id: 'default', - name: 'Default', - }, - ], - defaultActionGroupId: 'default', - minimumLicenseRequired: 'basic', - validate: { - params: schema.object({ - param1: schema.string(), - param2: schema.string({ defaultValue: 'default-value' }), - }), - }, - async executor() {}, - producer: 'alerts', - }, - { param1: 'value' } + { param1: 'value' }, + schema.object({ + param1: schema.string(), + param2: schema.string({ defaultValue: 'default-value' }), + }) ); expect(result).toEqual({ param1: 'value', @@ -63,26 +31,10 @@ test('should validate and apply defaults when params is valid', () => { test('should validate and throw error when params is invalid', () => { expect(() => validateAlertTypeParams( - { - id: 'my-alert-type', - name: 'My description', - actionGroups: [ - { - id: 'default', - name: 'Default', - }, - ], - defaultActionGroupId: 'default', - minimumLicenseRequired: 'basic', - validate: { - params: schema.object({ - param1: schema.string(), - }), - }, - async executor() {}, - producer: 'alerts', - }, - {} + {}, + schema.object({ + param1: schema.string(), + }) ) ).toThrowErrorMatchingInlineSnapshot( `"params invalid: [param1]: expected value of type [string] but got [undefined]"` diff --git a/x-pack/plugins/alerts/server/lib/validate_alert_type_params.ts b/x-pack/plugins/alerts/server/lib/validate_alert_type_params.ts index a443143d8cbde..2f510f90a2367 100644 --- a/x-pack/plugins/alerts/server/lib/validate_alert_type_params.ts +++ b/x-pack/plugins/alerts/server/lib/validate_alert_type_params.ts @@ -5,15 +5,14 @@ */ import Boom from '@hapi/boom'; -import { AlertType, AlertExecutorOptions } from '../types'; +import { AlertTypeParams, AlertTypeParamsValidator } from '../types'; -export function validateAlertTypeParams( - alertType: AlertType, - params: Record -): AlertExecutorOptions['params'] { - const validator = alertType.validate && alertType.validate.params; +export function validateAlertTypeParams( + params: Record, + validator?: AlertTypeParamsValidator +): Params { if (!validator) { - return params as AlertExecutorOptions['params']; + return params as Params; } try { diff --git a/x-pack/plugins/alerts/server/mocks.ts b/x-pack/plugins/alerts/server/mocks.ts index cfae4c650bd42..0f042b7a81d6c 100644 --- a/x-pack/plugins/alerts/server/mocks.ts +++ b/x-pack/plugins/alerts/server/mocks.ts @@ -11,6 +11,7 @@ import { elasticsearchServiceMock, savedObjectsClientMock, } from '../../../../src/core/server/mocks'; +import { AlertInstanceContext, AlertInstanceState } from './types'; export { alertsClientMock }; @@ -30,8 +31,14 @@ const createStartMock = () => { return mock; }; -export type AlertInstanceMock = jest.Mocked; -const createAlertInstanceFactoryMock = () => { +export type AlertInstanceMock< + State extends AlertInstanceState = AlertInstanceState, + Context extends AlertInstanceContext = AlertInstanceContext +> = jest.Mocked>; +const createAlertInstanceFactoryMock = < + InstanceState extends AlertInstanceState = AlertInstanceState, + InstanceContext extends AlertInstanceContext = AlertInstanceContext +>() => { const mock = { hasScheduledActions: jest.fn(), isThrottled: jest.fn(), @@ -50,14 +57,17 @@ const createAlertInstanceFactoryMock = () => { mock.unscheduleActions.mockReturnValue(mock); mock.scheduleActions.mockReturnValue(mock); - return (mock as unknown) as AlertInstanceMock; + return (mock as unknown) as AlertInstanceMock; }; -const createAlertServicesMock = () => { - const alertInstanceFactoryMock = createAlertInstanceFactoryMock(); +const createAlertServicesMock = < + InstanceState extends AlertInstanceState = AlertInstanceState, + InstanceContext extends AlertInstanceContext = AlertInstanceContext +>() => { + const alertInstanceFactoryMock = createAlertInstanceFactoryMock(); return { alertInstanceFactory: jest - .fn, [string]>() + .fn>, [string]>() .mockReturnValue(alertInstanceFactoryMock), callCluster: elasticsearchServiceMock.createLegacyScopedClusterClient().callAsCurrentUser, getLegacyScopedClusterClient: jest.fn(), diff --git a/x-pack/plugins/alerts/server/routes/create.test.ts b/x-pack/plugins/alerts/server/routes/create.test.ts index 5597b315158cd..fc531821f25b6 100644 --- a/x-pack/plugins/alerts/server/routes/create.test.ts +++ b/x-pack/plugins/alerts/server/routes/create.test.ts @@ -49,7 +49,7 @@ describe('createAlertRoute', () => { ], }; - const createResult: Alert = { + const createResult: Alert<{ bar: boolean }> = { ...mockedAlert, enabled: true, muteAll: false, diff --git a/x-pack/plugins/alerts/server/routes/create.ts b/x-pack/plugins/alerts/server/routes/create.ts index a34a3118985fa..a79a9d40b236f 100644 --- a/x-pack/plugins/alerts/server/routes/create.ts +++ b/x-pack/plugins/alerts/server/routes/create.ts @@ -16,7 +16,13 @@ import { ILicenseState } from '../lib/license_state'; import { verifyApiAccess } from '../lib/license_api_access'; import { validateDurationSchema } from '../lib'; import { handleDisabledApiKeysError } from './lib/error_handler'; -import { Alert, AlertNotifyWhenType, BASE_ALERT_API_PATH, validateNotifyWhenType } from '../types'; +import { + Alert, + AlertNotifyWhenType, + AlertTypeParams, + BASE_ALERT_API_PATH, + validateNotifyWhenType, +} from '../types'; import { AlertTypeDisabledError } from '../lib/errors/alert_type_disabled'; export const bodySchema = schema.object({ @@ -65,7 +71,9 @@ export const createAlertRoute = (router: IRouter, licenseState: ILicenseState) = const alert = req.body; const notifyWhen = alert?.notifyWhen ? (alert.notifyWhen as AlertNotifyWhenType) : null; try { - const alertRes: Alert = await alertsClient.create({ data: { ...alert, notifyWhen } }); + const alertRes: Alert = await alertsClient.create({ + data: { ...alert, notifyWhen }, + }); return res.ok({ body: alertRes, }); diff --git a/x-pack/plugins/alerts/server/routes/get.test.ts b/x-pack/plugins/alerts/server/routes/get.test.ts index 21e52ece82d2d..747f9b11e2b47 100644 --- a/x-pack/plugins/alerts/server/routes/get.test.ts +++ b/x-pack/plugins/alerts/server/routes/get.test.ts @@ -22,7 +22,9 @@ beforeEach(() => { }); describe('getAlertRoute', () => { - const mockedAlert: Alert = { + const mockedAlert: Alert<{ + bar: true; + }> = { id: '1', alertTypeId: '1', schedule: { interval: '10s' }, diff --git a/x-pack/plugins/alerts/server/task_runner/alert_task_instance.test.ts b/x-pack/plugins/alerts/server/task_runner/alert_task_instance.test.ts index 09236ec5e0ad1..1bd8b75e2133d 100644 --- a/x-pack/plugins/alerts/server/task_runner/alert_task_instance.test.ts +++ b/x-pack/plugins/alerts/server/task_runner/alert_task_instance.test.ts @@ -9,7 +9,9 @@ import { AlertTaskInstance, taskInstanceToAlertTaskInstance } from './alert_task import uuid from 'uuid'; import { SanitizedAlert } from '../types'; -const alert: SanitizedAlert = { +const alert: SanitizedAlert<{ + bar: boolean; +}> = { id: 'alert-123', alertTypeId: '123', schedule: { interval: '10s' }, diff --git a/x-pack/plugins/alerts/server/task_runner/alert_task_instance.ts b/x-pack/plugins/alerts/server/task_runner/alert_task_instance.ts index a290f3fa33c70..ab074cfdffa1c 100644 --- a/x-pack/plugins/alerts/server/task_runner/alert_task_instance.ts +++ b/x-pack/plugins/alerts/server/task_runner/alert_task_instance.ts @@ -13,6 +13,7 @@ import { alertParamsSchema, alertStateSchema, AlertTaskParams, + AlertTypeParams, } from '../../common'; export interface AlertTaskInstance extends ConcreteTaskInstance { @@ -23,9 +24,9 @@ export interface AlertTaskInstance extends ConcreteTaskInstance { const enumerateErrorFields = (e: t.Errors) => `${e.map(({ context }) => context.map(({ key }) => key).join('.'))}`; -export function taskInstanceToAlertTaskInstance( +export function taskInstanceToAlertTaskInstance( taskInstance: ConcreteTaskInstance, - alert?: SanitizedAlert + alert?: SanitizedAlert ): AlertTaskInstance { return { ...taskInstance, diff --git a/x-pack/plugins/alerts/server/task_runner/create_execution_handler.test.ts b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.test.ts index b414e726f0101..5603b13a3b1f5 100644 --- a/x-pack/plugins/alerts/server/task_runner/create_execution_handler.test.ts +++ b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.test.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AlertType } from '../types'; -import { createExecutionHandler } from './create_execution_handler'; +import { createExecutionHandler, CreateExecutionHandlerOptions } from './create_execution_handler'; import { loggingSystemMock } from '../../../../../src/core/server/mocks'; import { actionsMock, @@ -16,12 +15,19 @@ import { eventLoggerMock } from '../../../event_log/server/event_logger.mock'; import { KibanaRequest } from 'kibana/server'; import { asSavedObjectExecutionSource } from '../../../actions/server'; import { InjectActionParamsOpts } from './inject_action_params'; +import { UntypedNormalizedAlertType } from '../alert_type_registry'; +import { + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, +} from '../types'; jest.mock('./inject_action_params', () => ({ injectActionParams: jest.fn(), })); -const alertType: AlertType = { +const alertType: UntypedNormalizedAlertType = { id: 'test', name: 'Test', actionGroups: [ @@ -39,18 +45,26 @@ const alertType: AlertType = { }; const actionsClient = actionsClientMock.create(); -const createExecutionHandlerParams = { - actionsPlugin: actionsMock.createStart(), + +const mockActionsPlugin = actionsMock.createStart(); +const mockEventLogger = eventLoggerMock.create(); +const createExecutionHandlerParams: jest.Mocked< + CreateExecutionHandlerOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + > +> = { + actionsPlugin: mockActionsPlugin, spaceId: 'default', alertId: '1', alertName: 'name-of-alert', tags: ['tag-A', 'tag-B'], apiKey: 'MTIzOmFiYw==', - spaceIdToNamespace: jest.fn().mockReturnValue(undefined), - getBasePath: jest.fn().mockReturnValue(undefined), alertType, logger: loggingSystemMock.create().get(), - eventLogger: eventLoggerMock.create(), + eventLogger: mockEventLogger, actions: [ { id: '1', @@ -79,12 +93,10 @@ beforeEach(() => { .injectActionParams.mockImplementation( ({ actionParams }: InjectActionParamsOpts) => actionParams ); - createExecutionHandlerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); - createExecutionHandlerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); - createExecutionHandlerParams.actionsPlugin.getActionsClientWithRequest.mockResolvedValue( - actionsClient - ); - createExecutionHandlerParams.actionsPlugin.renderActionParameterTemplates.mockImplementation( + mockActionsPlugin.isActionTypeEnabled.mockReturnValue(true); + mockActionsPlugin.isActionExecutable.mockReturnValue(true); + mockActionsPlugin.getActionsClientWithRequest.mockResolvedValue(actionsClient); + mockActionsPlugin.renderActionParameterTemplates.mockImplementation( renderActionParameterTemplatesDefault ); }); @@ -97,9 +109,9 @@ test('enqueues execution per selected action', async () => { context: {}, alertInstanceId: '2', }); - expect( - createExecutionHandlerParams.actionsPlugin.getActionsClientWithRequest - ).toHaveBeenCalledWith(createExecutionHandlerParams.request); + expect(mockActionsPlugin.getActionsClientWithRequest).toHaveBeenCalledWith( + createExecutionHandlerParams.request + ); expect(actionsClient.enqueueExecution).toHaveBeenCalledTimes(1); expect(actionsClient.enqueueExecution.mock.calls[0]).toMatchInlineSnapshot(` Array [ @@ -124,9 +136,8 @@ test('enqueues execution per selected action', async () => { ] `); - const eventLogger = createExecutionHandlerParams.eventLogger; - expect(eventLogger.logEvent).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent.mock.calls).toMatchInlineSnapshot(` + expect(mockEventLogger.logEvent).toHaveBeenCalledTimes(1); + expect(mockEventLogger.logEvent.mock.calls).toMatchInlineSnapshot(` Array [ Array [ Object { @@ -171,9 +182,9 @@ test('enqueues execution per selected action', async () => { test(`doesn't call actionsPlugin.execute for disabled actionTypes`, async () => { // Mock two calls, one for check against actions[0] and the second for actions[1] - createExecutionHandlerParams.actionsPlugin.isActionExecutable.mockReturnValueOnce(false); - createExecutionHandlerParams.actionsPlugin.isActionTypeEnabled.mockReturnValueOnce(false); - createExecutionHandlerParams.actionsPlugin.isActionTypeEnabled.mockReturnValueOnce(true); + mockActionsPlugin.isActionExecutable.mockReturnValueOnce(false); + mockActionsPlugin.isActionTypeEnabled.mockReturnValueOnce(false); + mockActionsPlugin.isActionTypeEnabled.mockReturnValueOnce(true); const executionHandler = createExecutionHandler({ ...createExecutionHandlerParams, actions: [ @@ -214,9 +225,9 @@ test(`doesn't call actionsPlugin.execute for disabled actionTypes`, async () => }); test('trow error error message when action type is disabled', async () => { - createExecutionHandlerParams.actionsPlugin.preconfiguredActions = []; - createExecutionHandlerParams.actionsPlugin.isActionExecutable.mockReturnValue(false); - createExecutionHandlerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(false); + mockActionsPlugin.preconfiguredActions = []; + mockActionsPlugin.isActionExecutable.mockReturnValue(false); + mockActionsPlugin.isActionTypeEnabled.mockReturnValue(false); const executionHandler = createExecutionHandler({ ...createExecutionHandlerParams, actions: [ @@ -243,7 +254,7 @@ test('trow error error message when action type is disabled', async () => { expect(actionsClient.enqueueExecution).toHaveBeenCalledTimes(0); - createExecutionHandlerParams.actionsPlugin.isActionExecutable.mockImplementation(() => true); + mockActionsPlugin.isActionExecutable.mockImplementation(() => true); const executionHandlerForPreconfiguredAction = createExecutionHandler({ ...createExecutionHandlerParams, actions: [...createExecutionHandlerParams.actions], diff --git a/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts index 8c7ad79483194..8b4412aeb23e5 100644 --- a/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts +++ b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts @@ -15,14 +15,20 @@ import { EVENT_LOG_ACTIONS } from '../plugin'; import { injectActionParams } from './inject_action_params'; import { AlertAction, + AlertTypeParams, + AlertTypeState, AlertInstanceState, AlertInstanceContext, - AlertType, - AlertTypeParams, RawAlert, } from '../types'; +import { NormalizedAlertType } from '../alert_type_registry'; -interface CreateExecutionHandlerOptions { +export interface CreateExecutionHandlerOptions< + Params extends AlertTypeParams, + State extends AlertTypeState, + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +> { alertId: string; alertName: string; tags?: string[]; @@ -30,7 +36,7 @@ interface CreateExecutionHandlerOptions { actions: AlertAction[]; spaceId: string; apiKey: RawAlert['apiKey']; - alertType: AlertType; + alertType: NormalizedAlertType; logger: Logger; eventLogger: IEventLogger; request: KibanaRequest; @@ -45,7 +51,12 @@ interface ExecutionHandlerOptions { state: AlertInstanceState; } -export function createExecutionHandler({ +export function createExecutionHandler< + Params extends AlertTypeParams, + State extends AlertTypeState, + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +>({ logger, alertId, alertName, @@ -58,7 +69,7 @@ export function createExecutionHandler({ eventLogger, request, alertParams, -}: CreateExecutionHandlerOptions) { +}: CreateExecutionHandlerOptions) { const alertTypeActionGroups = new Map( alertType.actionGroups.map((actionGroup) => [actionGroup.id, actionGroup.name]) ); diff --git a/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts index a4b565194e431..967c5263b9730 100644 --- a/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts @@ -6,7 +6,13 @@ import sinon from 'sinon'; import { schema } from '@kbn/config-schema'; -import { AlertExecutorOptions } from '../types'; +import { + AlertExecutorOptions, + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, +} from '../types'; import { ConcreteTaskInstance, isUnrecoverableError, @@ -28,9 +34,9 @@ import { IEventLogger } from '../../../event_log/server'; import { SavedObjectsErrorHelpers } from '../../../../../src/core/server'; import { Alert, RecoveredActionGroup } from '../../common'; import { omit } from 'lodash'; -import { NormalizedAlertType } from '../alert_type_registry'; +import { UntypedNormalizedAlertType } from '../alert_type_registry'; import { alertTypeRegistryMock } from '../alert_type_registry.mock'; -const alertType = { +const alertType: jest.Mocked = { id: 'test', name: 'My test alert', actionGroups: [{ id: 'default', name: 'Default' }, RecoveredActionGroup], @@ -91,7 +97,7 @@ describe('Task Runner', () => { alertTypeRegistry, }; - const mockedAlertTypeSavedObject: Alert = { + const mockedAlertTypeSavedObject: Alert = { id: '1', consumer: 'bar', createdAt: new Date('2019-02-12T21:01:22.479Z'), @@ -150,7 +156,7 @@ describe('Task Runner', () => { test('successfully executes the task', async () => { const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, { ...mockedTaskInstance, state: { @@ -254,14 +260,21 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices .alertInstanceFactory('1') .scheduleActionsWithSubGroup('default', 'subDefault'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -407,12 +420,19 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -516,13 +536,20 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); executorServices.alertInstanceFactory('2').scheduleActions('default'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -562,12 +589,19 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, { ...mockedTaskInstance, state: { @@ -656,12 +690,19 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, { ...mockedTaskInstance, state: { @@ -696,14 +737,21 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices .alertInstanceFactory('1') .scheduleActionsWithSubGroup('default', 'subgroup1'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, { ...mockedTaskInstance, state: { @@ -744,12 +792,19 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -912,12 +967,19 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, { ...mockedTaskInstance, state: { @@ -1012,12 +1074,19 @@ describe('Task Runner', () => { }; alertTypeWithCustomRecovery.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } ); const taskRunner = new TaskRunner( - alertTypeWithCustomRecovery as NormalizedAlertType, + alertTypeWithCustomRecovery, { ...mockedTaskInstance, state: { @@ -1103,13 +1172,20 @@ describe('Task Runner', () => { test('persists alertInstances passed in from state, only if they are scheduled for execution', async () => { alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } ); const date = new Date().toISOString(); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, { ...mockedTaskInstance, state: { @@ -1239,7 +1315,7 @@ describe('Task Runner', () => { param1: schema.string(), }), }, - } as NormalizedAlertType, + }, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1267,7 +1343,7 @@ describe('Task Runner', () => { test('uses API key when provided', async () => { const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1300,7 +1376,7 @@ describe('Task Runner', () => { test(`doesn't use API key when not provided`, async () => { const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1330,7 +1406,7 @@ describe('Task Runner', () => { test('rescheduled the Alert if the schedule has update during a task run', async () => { const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1365,13 +1441,20 @@ describe('Task Runner', () => { test('recovers gracefully when the AlertType executor throws an exception', async () => { alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { throw new Error('OMG'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1438,7 +1521,7 @@ describe('Task Runner', () => { }); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1497,7 +1580,7 @@ describe('Task Runner', () => { }); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1564,7 +1647,7 @@ describe('Task Runner', () => { }); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1631,7 +1714,7 @@ describe('Task Runner', () => { }); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1701,7 +1784,7 @@ describe('Task Runner', () => { const legacyTaskInstance = omit(mockedTaskInstance, 'schedule'); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, legacyTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1733,13 +1816,20 @@ describe('Task Runner', () => { }; alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { throw new Error('OMG'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, { ...mockedTaskInstance, state: originalAlertSate, @@ -1770,7 +1860,7 @@ describe('Task Runner', () => { }); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); diff --git a/x-pack/plugins/alerts/server/task_runner/task_runner.ts b/x-pack/plugins/alerts/server/task_runner/task_runner.ts index 44cf7dd91be7d..c4187145e5a16 100644 --- a/x-pack/plugins/alerts/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerts/server/task_runner/task_runner.ts @@ -26,7 +26,6 @@ import { RawAlertInstance, AlertTaskState, Alert, - AlertExecutorOptions, SanitizedAlert, AlertExecutionStatus, AlertExecutionStatusErrorReasons, @@ -39,7 +38,13 @@ import { IEvent, IEventLogger, SAVED_OBJECT_REL_PRIMARY } from '../../../event_l import { isAlertSavedObjectNotFoundError } from '../lib/is_alert_not_found_error'; import { AlertsClient } from '../alerts_client'; import { partiallyUpdateAlert } from '../saved_objects'; -import { ActionGroup } from '../../common'; +import { + ActionGroup, + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, +} from '../../common'; import { NormalizedAlertType } from '../alert_type_registry'; const FALLBACK_RETRY_INTERVAL = '5m'; @@ -55,15 +60,20 @@ interface AlertTaskInstance extends ConcreteTaskInstance { state: AlertTaskState; } -export class TaskRunner { +export class TaskRunner< + Params extends AlertTypeParams, + State extends AlertTypeState, + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +> { private context: TaskRunnerContext; private logger: Logger; private taskInstance: AlertTaskInstance; - private alertType: NormalizedAlertType; + private alertType: NormalizedAlertType; private readonly alertTypeRegistry: AlertTypeRegistry; constructor( - alertType: NormalizedAlertType, + alertType: NormalizedAlertType, taskInstance: ConcreteTaskInstance, context: TaskRunnerContext ) { @@ -131,8 +141,8 @@ export class TaskRunner { tags: string[] | undefined, spaceId: string, apiKey: RawAlert['apiKey'], - actions: Alert['actions'], - alertParams: RawAlert['params'] + actions: Alert['actions'], + alertParams: Params ) { return createExecutionHandler({ alertId, @@ -152,7 +162,7 @@ export class TaskRunner { async executeAlertInstance( alertInstanceId: string, - alertInstance: AlertInstance, + alertInstance: AlertInstance, executionHandler: ReturnType ) { const { @@ -168,8 +178,8 @@ export class TaskRunner { async executeAlertInstances( services: Services, - alert: SanitizedAlert, - params: AlertExecutorOptions['params'], + alert: SanitizedAlert, + params: Params, executionHandler: ReturnType, spaceId: string, event: Event @@ -190,9 +200,12 @@ export class TaskRunner { } = this.taskInstance; const namespace = this.context.spaceIdToNamespace(spaceId); - const alertInstances = mapValues, AlertInstance>( + const alertInstances = mapValues< + Record, + AlertInstance + >( alertRawInstances, - (rawAlertInstance) => new AlertInstance(rawAlertInstance) + (rawAlertInstance) => new AlertInstance(rawAlertInstance) ); const originalAlertInstances = cloneDeep(alertInstances); @@ -205,10 +218,12 @@ export class TaskRunner { alertId, services: { ...services, - alertInstanceFactory: createAlertInstanceFactory(alertInstances), + alertInstanceFactory: createAlertInstanceFactory( + alertInstances + ), }, params, - state: alertTypeState, + state: alertTypeState as State, startedAt: this.taskInstance.startedAt!, previousStartedAt: previousStartedAt ? new Date(previousStartedAt) : null, spaceId, @@ -232,12 +247,15 @@ export class TaskRunner { event.event.outcome = 'success'; // Cleanup alert instances that are no longer scheduling actions to avoid over populating the alertInstances object - const instancesWithScheduledActions = pickBy(alertInstances, (alertInstance: AlertInstance) => - alertInstance.hasScheduledActions() + const instancesWithScheduledActions = pickBy( + alertInstances, + (alertInstance: AlertInstance) => + alertInstance.hasScheduledActions() ); const recoveredAlertInstances = pickBy( alertInstances, - (alertInstance: AlertInstance) => !alertInstance.hasScheduledActions() + (alertInstance: AlertInstance) => + !alertInstance.hasScheduledActions() ); logActiveAndRecoveredInstances({ @@ -272,7 +290,10 @@ export class TaskRunner { const instancesToExecute = notifyWhen === 'onActionGroupChange' ? Object.entries(instancesWithScheduledActions).filter( - ([alertInstanceName, alertInstance]: [string, AlertInstance]) => { + ([alertInstanceName, alertInstance]: [ + string, + AlertInstance + ]) => { const shouldExecuteAction = alertInstance.scheduledActionGroupOrSubgroupHasChanged(); if (!shouldExecuteAction) { this.logger.debug( @@ -283,7 +304,10 @@ export class TaskRunner { } ) : Object.entries(instancesWithScheduledActions).filter( - ([alertInstanceName, alertInstance]: [string, AlertInstance]) => { + ([alertInstanceName, alertInstance]: [ + string, + AlertInstance + ]) => { const throttled = alertInstance.isThrottled(throttle); const muted = mutedInstanceIdsSet.has(alertInstanceName); const shouldExecuteAction = !throttled && !muted; @@ -299,8 +323,9 @@ export class TaskRunner { ); await Promise.all( - instancesToExecute.map(([id, alertInstance]: [string, AlertInstance]) => - this.executeAlertInstance(id, alertInstance, executionHandler) + instancesToExecute.map( + ([id, alertInstance]: [string, AlertInstance]) => + this.executeAlertInstance(id, alertInstance, executionHandler) ) ); } else { @@ -309,17 +334,17 @@ export class TaskRunner { return { alertTypeState: updatedAlertTypeState || undefined, - alertInstances: mapValues, RawAlertInstance>( - instancesWithScheduledActions, - (alertInstance) => alertInstance.toRaw() - ), + alertInstances: mapValues< + Record>, + RawAlertInstance + >(instancesWithScheduledActions, (alertInstance) => alertInstance.toRaw()), }; } async validateAndExecuteAlert( services: Services, apiKey: RawAlert['apiKey'], - alert: SanitizedAlert, + alert: SanitizedAlert, event: Event ) { const { @@ -327,7 +352,7 @@ export class TaskRunner { } = this.taskInstance; // Validate - const validatedParams = validateAlertTypeParams(this.alertType, alert.params); + const validatedParams = validateAlertTypeParams(alert.params, this.alertType.validate?.params); const executionHandler = this.getExecutionHandler( alertId, alert.name, @@ -359,7 +384,7 @@ export class TaskRunner { } const [services, alertsClient] = this.getServicesWithSpaceLevelPermissions(spaceId, apiKey); - let alert: SanitizedAlert; + let alert: SanitizedAlert; // Ensure API key is still valid and user has access try { @@ -501,19 +526,23 @@ export class TaskRunner { } } -interface GenerateNewAndRecoveredInstanceEventsParams { +interface GenerateNewAndRecoveredInstanceEventsParams< + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +> { eventLogger: IEventLogger; - originalAlertInstances: Dictionary; - currentAlertInstances: Dictionary; - recoveredAlertInstances: Dictionary; + originalAlertInstances: Dictionary>; + currentAlertInstances: Dictionary>; + recoveredAlertInstances: Dictionary>; alertId: string; alertLabel: string; namespace: string | undefined; } -function generateNewAndRecoveredInstanceEvents( - params: GenerateNewAndRecoveredInstanceEventsParams -) { +function generateNewAndRecoveredInstanceEvents< + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +>(params: GenerateNewAndRecoveredInstanceEventsParams) { const { eventLogger, alertId, @@ -584,16 +613,22 @@ function generateNewAndRecoveredInstanceEvents( } } -interface ScheduleActionsForRecoveredInstancesParams { +interface ScheduleActionsForRecoveredInstancesParams< + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +> { logger: Logger; recoveryActionGroup: ActionGroup; - recoveredAlertInstances: Dictionary; + recoveredAlertInstances: Dictionary>; executionHandler: ReturnType; mutedInstanceIdsSet: Set; alertLabel: string; } -function scheduleActionsForRecoveredInstances(params: ScheduleActionsForRecoveredInstancesParams) { +function scheduleActionsForRecoveredInstances< + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +>(params: ScheduleActionsForRecoveredInstancesParams) { const { logger, recoveryActionGroup, @@ -623,14 +658,20 @@ function scheduleActionsForRecoveredInstances(params: ScheduleActionsForRecovere } } -interface LogActiveAndRecoveredInstancesParams { +interface LogActiveAndRecoveredInstancesParams< + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +> { logger: Logger; - activeAlertInstances: Dictionary; - recoveredAlertInstances: Dictionary; + activeAlertInstances: Dictionary>; + recoveredAlertInstances: Dictionary>; alertLabel: string; } -function logActiveAndRecoveredInstances(params: LogActiveAndRecoveredInstancesParams) { +function logActiveAndRecoveredInstances< + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +>(params: LogActiveAndRecoveredInstancesParams) { const { logger, activeAlertInstances, recoveredAlertInstances, alertLabel } = params; const activeInstanceIds = Object.keys(activeAlertInstances); const recoveredInstanceIds = Object.keys(recoveredAlertInstances); diff --git a/x-pack/plugins/alerts/server/task_runner/task_runner_factory.test.ts b/x-pack/plugins/alerts/server/task_runner/task_runner_factory.test.ts index 6c58b64fffa92..3a5a130f582ed 100644 --- a/x-pack/plugins/alerts/server/task_runner/task_runner_factory.test.ts +++ b/x-pack/plugins/alerts/server/task_runner/task_runner_factory.test.ts @@ -16,10 +16,10 @@ import { import { actionsMock } from '../../../actions/server/mocks'; import { alertsMock, alertsClientMock } from '../mocks'; import { eventLoggerMock } from '../../../event_log/server/event_logger.mock'; -import { NormalizedAlertType } from '../alert_type_registry'; +import { UntypedNormalizedAlertType } from '../alert_type_registry'; import { alertTypeRegistryMock } from '../alert_type_registry.mock'; -const alertType: NormalizedAlertType = { +const alertType: UntypedNormalizedAlertType = { id: 'test', name: 'My test alert', actionGroups: [{ id: 'default', name: 'Default' }], diff --git a/x-pack/plugins/alerts/server/task_runner/task_runner_factory.ts b/x-pack/plugins/alerts/server/task_runner/task_runner_factory.ts index 1fe94972bd4b0..e266608d80880 100644 --- a/x-pack/plugins/alerts/server/task_runner/task_runner_factory.ts +++ b/x-pack/plugins/alerts/server/task_runner/task_runner_factory.ts @@ -17,7 +17,7 @@ import { AlertTypeRegistry, GetServicesFunction, SpaceIdToNamespaceFunction } fr import { TaskRunner } from './task_runner'; import { IEventLogger } from '../../../event_log/server'; import { AlertsClient } from '../alerts_client'; -import { NormalizedAlertType } from '../alert_type_registry'; +import { UntypedNormalizedAlertType } from '../alert_type_registry'; export interface TaskRunnerContext { logger: Logger; @@ -44,7 +44,7 @@ export class TaskRunnerFactory { this.taskRunnerContext = taskRunnerContext; } - public create(alertType: NormalizedAlertType, { taskInstance }: RunContext) { + public create(alertType: UntypedNormalizedAlertType, { taskInstance }: RunContext) { if (!this.isInitialized) { throw new Error('TaskRunnerFactory not initialized'); } diff --git a/x-pack/plugins/alerts/server/types.ts b/x-pack/plugins/alerts/server/types.ts index 8704068c3e51a..bb2d429a7c8b5 100644 --- a/x-pack/plugins/alerts/server/types.ts +++ b/x-pack/plugins/alerts/server/types.ts @@ -47,6 +47,9 @@ declare module 'src/core/server' { } export interface Services { + /** + * @deprecated Use `scopedClusterClient` instead. + */ callCluster: ILegacyScopedClusterClient['callAsCurrentUser']; savedObjectsClient: SavedObjectsClientContract; scopedClusterClient: ElasticsearchClient; @@ -61,10 +64,10 @@ export interface AlertServices< } export interface AlertExecutorOptions< - Params extends AlertTypeParams = AlertTypeParams, - State extends AlertTypeState = AlertTypeState, - InstanceState extends AlertInstanceState = AlertInstanceState, - InstanceContext extends AlertInstanceContext = AlertInstanceContext + Params extends AlertTypeParams = never, + State extends AlertTypeState = never, + InstanceState extends AlertInstanceState = never, + InstanceContext extends AlertInstanceContext = never > { alertId: string; startedAt: Date; @@ -85,26 +88,28 @@ export interface ActionVariable { description: string; } -// signature of the alert type executor function export type ExecutorType< - Params, - State, - InstanceState extends AlertInstanceState = AlertInstanceState, - InstanceContext extends AlertInstanceContext = AlertInstanceContext + Params extends AlertTypeParams = never, + State extends AlertTypeState = never, + InstanceState extends AlertInstanceState = never, + InstanceContext extends AlertInstanceContext = never > = ( options: AlertExecutorOptions ) => Promise; +export interface AlertTypeParamsValidator { + validate: (object: unknown) => Params; +} export interface AlertType< - Params extends AlertTypeParams = AlertTypeParams, - State extends AlertTypeState = AlertTypeState, - InstanceState extends AlertInstanceState = AlertInstanceState, - InstanceContext extends AlertInstanceContext = AlertInstanceContext + Params extends AlertTypeParams = never, + State extends AlertTypeState = never, + InstanceState extends AlertInstanceState = never, + InstanceContext extends AlertInstanceContext = never > { id: string; name: string; validate?: { - params?: { validate: (object: unknown) => Params }; + params?: AlertTypeParamsValidator; }; actionGroups: ActionGroup[]; defaultActionGroupId: ActionGroup['id']; @@ -119,6 +124,13 @@ export interface AlertType< minimumLicenseRequired: LicenseType; } +export type UntypedAlertType = AlertType< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext +>; + export interface RawAlertAction extends SavedObjectAttributes { group: string; actionRef: string; @@ -142,7 +154,8 @@ export interface RawAlertExecutionStatus extends SavedObjectAttributes { }; } -export type PartialAlert = Pick & Partial>; +export type PartialAlert = Pick, 'id'> & + Partial, 'id'>>; export interface RawAlert extends SavedObjectAttributes { enabled: boolean; diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/__tests__/KeyUXMetrics.test.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/__tests__/KeyUXMetrics.test.tsx index baa9cb7dd74f9..5d73cbc4cd3c8 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/__tests__/KeyUXMetrics.test.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/__tests__/KeyUXMetrics.test.tsx @@ -23,7 +23,7 @@ describe('KeyUXMetrics', () => { ), }, - { - width: px(units.double), - name: '', - render: (config: Config) => ( - - ), - }, - { - width: px(units.double), - name: '', - render: (config: Config) => ( - setConfigToBeDeleted(config)} - /> - ), - }, + ...(canSave + ? [ + { + width: px(units.double), + name: '', + render: (config: Config) => ( + + ), + }, + { + width: px(units.double), + name: '', + render: (config: Config) => ( + setConfigToBeDeleted(config)} + /> + ), + }, + ] + : []), ]; return ( diff --git a/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/index.tsx index c408d5e960cf3..c1f5ec154792d 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/index.tsx @@ -3,6 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import { EuiToolTip } from '@elastic/eui'; import { EuiButton, EuiFlexGroup, @@ -73,15 +74,35 @@ function CreateConfigurationButton() { const { basePath } = core.http; const { search } = useLocation(); const href = createAgentConfigurationHref(search, basePath); + const canSave = core.application.capabilities.apm.save; return ( - - {i18n.translate('xpack.apm.agentConfig.createConfigButtonLabel', { - defaultMessage: 'Create configuration', - })} - + + + {i18n.translate('xpack.apm.agentConfig.createConfigButtonLabel', { + defaultMessage: 'Create configuration', + })} + + diff --git a/x-pack/plugins/apm/public/components/app/Settings/ApmIndices/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/ApmIndices/index.tsx index 5a5d20cde9ade..ba08af32d65b6 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/ApmIndices/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/ApmIndices/index.tsx @@ -4,25 +4,26 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useState, useEffect } from 'react'; -import { i18n } from '@kbn/i18n'; import { - EuiTitle, + EuiButton, + EuiButtonEmpty, + EuiFieldText, EuiFlexGroup, EuiFlexItem, + EuiForm, + EuiFormRow, EuiPanel, EuiSpacer, EuiText, - EuiForm, - EuiFormRow, - EuiFieldText, - EuiButton, - EuiButtonEmpty, + EuiTitle, + EuiToolTip, } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React, { useEffect, useState } from 'react'; +import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; import { useFetcher } from '../../../../hooks/use_fetcher'; -import { callApmApi } from '../../../../services/rest/createCallApmApi'; import { clearCache } from '../../../../services/rest/callApi'; -import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; +import { callApmApi } from '../../../../services/rest/createCallApmApi'; const APM_INDEX_LABELS = [ { @@ -85,17 +86,22 @@ async function saveApmIndices({ const INITIAL_STATE = [] as []; export function ApmIndices() { - const { toasts } = useApmPluginContext().core.notifications; + const { core } = useApmPluginContext(); + const { notifications, application } = core; + const canSave = application.capabilities.apm.save; const [apmIndices, setApmIndices] = useState>({}); const [isSaving, setIsSaving] = useState(false); - const { data = INITIAL_STATE, status, refetch } = useFetcher( - (_callApmApi) => - _callApmApi({ - endpoint: `GET /api/apm/settings/apm-index-settings`, - }), - [] + const { data = INITIAL_STATE, refetch } = useFetcher( + (_callApmApi) => { + if (canSave) { + return _callApmApi({ + endpoint: `GET /api/apm/settings/apm-index-settings`, + }); + } + }, + [canSave] ); useEffect(() => { @@ -119,7 +125,7 @@ export function ApmIndices() { setIsSaving(true); try { await saveApmIndices({ apmIndices }); - toasts.addSuccess({ + notifications.toasts.addSuccess({ title: i18n.translate( 'xpack.apm.settings.apmIndices.applyChanges.succeeded.title', { defaultMessage: 'Indices applied' } @@ -133,7 +139,7 @@ export function ApmIndices() { ), }); } catch (error) { - toasts.addDanger({ + notifications.toasts.addDanger({ title: i18n.translate( 'xpack.apm.settings.apmIndices.applyChanges.failed.title', { defaultMessage: 'Indices could not be applied.' } @@ -205,6 +211,7 @@ export function ApmIndices() { fullWidth > - - {i18n.translate( - 'xpack.apm.settings.apmIndices.applyButton', - { defaultMessage: 'Apply changes' } - )} - + + {i18n.translate( + 'xpack.apm.settings.apmIndices.applyButton', + { defaultMessage: 'Apply changes' } + )} + + diff --git a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateCustomLinkButton.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateCustomLinkButton.tsx index 56b3eaf425af7..3b4c127aab1e5 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateCustomLinkButton.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateCustomLinkButton.tsx @@ -3,17 +3,40 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { EuiButton } from '@elastic/eui'; +import { EuiButton, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { useApmPluginContext } from '../../../../../context/apm_plugin/use_apm_plugin_context'; export function CreateCustomLinkButton({ onClick }: { onClick: () => void }) { + const { core } = useApmPluginContext(); + const canSave = core.application.capabilities.apm.save; return ( - - {i18n.translate( - 'xpack.apm.settings.customizeUI.customLink.createCustomLink', - { defaultMessage: 'Create custom link' } - )} - + + + {i18n.translate( + 'xpack.apm.settings.customizeUI.customLink.createCustomLink', + { defaultMessage: 'Create custom link' } + )} + + ); } diff --git a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkTable.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkTable.tsx index d512ea19c7892..4bc1adee04bf4 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkTable.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkTable.tsx @@ -13,6 +13,7 @@ import { EuiSpacer, } from '@elastic/eui'; import { isEmpty } from 'lodash'; +import { useApmPluginContext } from '../../../../../context/apm_plugin/use_apm_plugin_context'; import { CustomLink } from '../../../../../../common/custom_link/custom_link_types'; import { units, px } from '../../../../../style/variables'; import { ManagedTable } from '../../../../shared/ManagedTable'; @@ -26,6 +27,8 @@ interface Props { export function CustomLinkTable({ items = [], onCustomLinkSelected }: Props) { const [searchTerm, setSearchTerm] = useState(''); + const { core } = useApmPluginContext(); + const canSave = core.application.capabilities.apm.save; const columns = [ { @@ -61,22 +64,26 @@ export function CustomLinkTable({ items = [], onCustomLinkSelected }: Props) { width: px(units.triple), name: '', actions: [ - { - name: i18n.translate( - 'xpack.apm.settings.customizeUI.customLink.table.editButtonLabel', - { defaultMessage: 'Edit' } - ), - description: i18n.translate( - 'xpack.apm.settings.customizeUI.customLink.table.editButtonDescription', - { defaultMessage: 'Edit this custom link' } - ), - icon: 'pencil', - color: 'primary', - type: 'icon', - onClick: (customLink: CustomLink) => { - onCustomLinkSelected(customLink); - }, - }, + ...(canSave + ? [ + { + name: i18n.translate( + 'xpack.apm.settings.customizeUI.customLink.table.editButtonLabel', + { defaultMessage: 'Edit' } + ), + description: i18n.translate( + 'xpack.apm.settings.customizeUI.customLink.table.editButtonDescription', + { defaultMessage: 'Edit this custom link' } + ), + icon: 'pencil', + color: 'primary', + type: 'icon', + onClick: (customLink: CustomLink) => { + onCustomLinkSelected(customLink); + }, + }, + ] + : []), ], }, ]; diff --git a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.test.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.test.tsx index 1da7d415b5660..4477ee5a99be3 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.test.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.test.tsx @@ -7,22 +7,26 @@ import { fireEvent, render, - waitFor, RenderResult, + waitFor, } from '@testing-library/react'; import React from 'react'; import { act } from 'react-dom/test-utils'; -import * as apmApi from '../../../../../services/rest/createCallApmApi'; +import { CustomLinkOverview } from '.'; import { License } from '../../../../../../../licensing/common/license'; -import * as hooks from '../../../../../hooks/use_fetcher'; +import { ApmPluginContextValue } from '../../../../../context/apm_plugin/apm_plugin_context'; +import { + mockApmPluginContextValue, + MockApmPluginContextWrapper, +} from '../../../../../context/apm_plugin/mock_apm_plugin_context'; import { LicenseContext } from '../../../../../context/license/license_context'; -import { CustomLinkOverview } from '.'; +import * as hooks from '../../../../../hooks/use_fetcher'; +import * as apmApi from '../../../../../services/rest/createCallApmApi'; import { expectTextsInDocument, expectTextsNotInDocument, } from '../../../../../utils/testHelpers'; import * as saveCustomLink from './CreateEditCustomLinkFlyout/saveCustomLink'; -import { MockApmPluginContextWrapper } from '../../../../../context/apm_plugin/mock_apm_plugin_context'; const data = [ { @@ -39,6 +43,16 @@ const data = [ }, ]; +function getMockAPMContext({ canSave }: { canSave: boolean }) { + return ({ + ...mockApmPluginContextValue, + core: { + ...mockApmPluginContextValue.core, + application: { capabilities: { apm: { save: canSave }, ml: {} } }, + }, + } as unknown) as ApmPluginContextValue; +} + describe('CustomLink', () => { beforeAll(() => { jest.spyOn(apmApi, 'callApmApi').mockResolvedValue({}); @@ -70,9 +84,11 @@ describe('CustomLink', () => { }); it('shows when no link is available', () => { const component = render( - - - + + + + + ); expectTextsInDocument(component, ['No links found.']); }); @@ -91,6 +107,34 @@ describe('CustomLink', () => { jest.clearAllMocks(); }); + it('enables create button when user has writte privileges', () => { + const mockContext = getMockAPMContext({ canSave: true }); + + const { getByTestId } = render( + + + + + + ); + const createButton = getByTestId('createButton') as HTMLButtonElement; + expect(createButton.disabled).toBeFalsy(); + }); + + it('enables edit button on custom link table when user has writte privileges', () => { + const mockContext = getMockAPMContext({ canSave: true }); + + const { getAllByText } = render( + + + + + + ); + + expect(getAllByText('Edit').length).toEqual(2); + }); + it('shows a table with all custom link', () => { const component = render( @@ -108,9 +152,11 @@ describe('CustomLink', () => { }); it('checks if create custom link button is available and working', () => { + const mockContext = getMockAPMContext({ canSave: true }); + const { queryByText, getByText } = render( - + @@ -137,9 +183,10 @@ describe('CustomLink', () => { }); const openFlyout = () => { + const mockContext = getMockAPMContext({ canSave: true }); const component = render( - + @@ -173,9 +220,10 @@ describe('CustomLink', () => { }); it('deletes a custom link', async () => { + const mockContext = getMockAPMContext({ canSave: true }); const component = render( - + @@ -356,4 +404,34 @@ describe('CustomLink', () => { expectTextsNotInDocument(component, ['Start free 30-day trial']); }); }); + + describe('with read-only user', () => { + it('disables create custom link button', () => { + const mockContext = getMockAPMContext({ canSave: false }); + + const { getByTestId } = render( + + + + + + ); + const createButton = getByTestId('createButton') as HTMLButtonElement; + expect(createButton.disabled).toBeTruthy(); + }); + + it('removes edit button on custom link table', () => { + const mockContext = getMockAPMContext({ canSave: false }); + + const { queryAllByText } = render( + + + + + + ); + + expect(queryAllByText('Edit').length).toEqual(0); + }); + }); }); diff --git a/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/jobs_list.tsx b/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/jobs_list.tsx index acc2550930b8e..b185685f0720a 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/jobs_list.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/jobs_list.tsx @@ -4,26 +4,26 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; import { - EuiPanel, - EuiTitle, - EuiText, - EuiSpacer, EuiButton, EuiFlexGroup, EuiFlexItem, + EuiPanel, + EuiSpacer, + EuiText, + EuiTitle, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; +import React from 'react'; +import { getEnvironmentLabel } from '../../../../../common/environment_filter_values'; import { FETCH_STATUS } from '../../../../hooks/use_fetcher'; -import { ITableColumn, ManagedTable } from '../../../shared/ManagedTable'; -import { LoadingStatePrompt } from '../../../shared/LoadingStatePrompt'; -import { MLSingleMetricLink } from '../../../shared/Links/MachineLearningLinks/MLSingleMetricLink'; +import { MLExplorerLink } from '../../../shared/Links/MachineLearningLinks/MLExplorerLink'; import { MLManageJobsLink } from '../../../shared/Links/MachineLearningLinks/MLManageJobsLink'; -import { getEnvironmentLabel } from '../../../../../common/environment_filter_values'; -import { LegacyJobsCallout } from './legacy_jobs_callout'; +import { LoadingStatePrompt } from '../../../shared/LoadingStatePrompt'; +import { ITableColumn, ManagedTable } from '../../../shared/ManagedTable'; import { AnomalyDetectionApiResponse } from './index'; +import { LegacyJobsCallout } from './legacy_jobs_callout'; type Jobs = AnomalyDetectionApiResponse['jobs']; @@ -44,14 +44,14 @@ const columns: Array> = [ { defaultMessage: 'Action' } ), render: (jobId: string) => ( - + {i18n.translate( 'xpack.apm.settings.anomalyDetection.jobList.mlJobLinkText', { defaultMessage: 'View job in ML', } )} - + ), }, ]; diff --git a/x-pack/plugins/apm/public/components/app/TraceOverview/TraceList.tsx b/x-pack/plugins/apm/public/components/app/TraceOverview/TraceList.tsx index e68f8a9809bf5..eebd03772f238 100644 --- a/x-pack/plugins/apm/public/components/app/TraceOverview/TraceList.tsx +++ b/x-pack/plugins/apm/public/components/app/TraceOverview/TraceList.tsx @@ -44,7 +44,7 @@ const traceListColumns: Array> = [ _: string, { serviceName, transactionName, transactionType }: TraceGroup ) => ( - + void; + onClick: () => void; onClose: () => void; detailsFetchStatus: FETCH_STATUS; isOpen: boolean; @@ -27,7 +27,7 @@ export function IconPopover({ icon, title, children, - onOpen, + onClick, onClose, detailsFetchStatus, isOpen, @@ -44,7 +44,7 @@ export function IconPopover({ anchorPosition="downCenter" ownFocus={false} button={ - + } diff --git a/x-pack/plugins/apm/public/components/app/service_details/service_icons/index.tsx b/x-pack/plugins/apm/public/components/app/service_details/service_icons/index.tsx index 327198e46131f..f6a712c562bff 100644 --- a/x-pack/plugins/apm/public/components/app/service_details/service_icons/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_details/service_icons/index.tsx @@ -82,6 +82,7 @@ export function ServiceIcons({ serviceName }: Props) { (callApmApi) => { if (selectedIconPopover && serviceName && start && end) { return callApmApi({ + isCachable: true, endpoint: 'GET /api/apm/services/{serviceName}/metadata/details', params: { path: { serviceName }, @@ -143,8 +144,10 @@ export function ServiceIcons({ serviceName }: Props) { icon={item.icon} detailsFetchStatus={detailsFetchStatus} title={item.title} - onOpen={() => { - setSelectedIconPopover(item.key); + onClick={() => { + setSelectedIconPopover((prevSelectedIconPopover) => + item.key === prevSelectedIconPopover ? null : item.key + ); }} onClose={() => { setSelectedIconPopover(null); diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/__fixtures__/props.json b/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/__fixtures__/props.json deleted file mode 100644 index 2e213c44bccf0..0000000000000 --- a/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/__fixtures__/props.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "items": [ - { - "serviceName": "opbeans-node", - "agentName": "nodejs", - "transactionsPerMinute": { - "value": 0, - "timeseries": [] - }, - "errorsPerMinute": { - "value": 46.06666666666667, - "timeseries": [] - }, - "environments": ["test"] - }, - { - "serviceName": "opbeans-python", - "agentName": "python", - "transactionsPerMinute": { - "value": 86.93333333333334, - "timeseries": [] - }, - "errorsPerMinute": { - "value": 12.6, - "timeseries": [] - }, - "avgResponseTime": { - "value": 91535.42944785276, - "timeseries": [] - }, - "environments": [] - } - ] -} diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/__fixtures__/service_api_mock_data.ts b/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/__fixtures__/service_api_mock_data.ts new file mode 100644 index 0000000000000..04e1c9f8cbcab --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/__fixtures__/service_api_mock_data.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { APIReturnType } from '../../../../../services/rest/createCallApmApi'; + +type ServiceListAPIResponse = APIReturnType<'GET /api/apm/services'>; + +export const items: ServiceListAPIResponse['items'] = [ + { + serviceName: 'opbeans-node', + transactionType: 'request', + agentName: 'nodejs', + transactionsPerMinute: { value: 0, timeseries: [] }, + transactionErrorRate: { value: 46.06666666666667, timeseries: [] }, + avgResponseTime: { value: null, timeseries: [] }, + environments: ['test'], + }, + { + serviceName: 'opbeans-python', + transactionType: 'page-load', + agentName: 'python', + transactionsPerMinute: { value: 86.93333333333334, timeseries: [] }, + transactionErrorRate: { value: 12.6, timeseries: [] }, + avgResponseTime: { value: 91535.42944785276, timeseries: [] }, + environments: [], + }, +]; diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/index.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/index.tsx index 157d3ecc738a1..175fad2993782 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/index.tsx @@ -6,10 +6,16 @@ import { EuiFlexItem, EuiFlexGroup, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React from 'react'; +import React, { useMemo } from 'react'; import styled from 'styled-components'; import { ValuesType } from 'utility-types'; import { orderBy } from 'lodash'; +import { EuiIcon } from '@elastic/eui'; +import { EuiText } from '@elastic/eui'; +import { + TRANSACTION_PAGE_LOAD, + TRANSACTION_REQUEST, +} from '../../../../../common/transaction_types'; import { APIReturnType } from '../../../../services/rest/createCallApmApi'; import { ServiceHealthStatus } from '../../../../../common/service_health_status'; import { @@ -55,126 +61,6 @@ const ToolTipWrapper = styled.span` } `; -export const SERVICE_COLUMNS: Array> = [ - { - field: 'healthStatus', - name: i18n.translate('xpack.apm.servicesTable.healthColumnLabel', { - defaultMessage: 'Health', - }), - width: px(unit * 6), - sortable: true, - render: (_, { healthStatus }) => { - return ( - - ); - }, - }, - { - field: 'serviceName', - name: i18n.translate('xpack.apm.servicesTable.nameColumnLabel', { - defaultMessage: 'Name', - }), - width: '40%', - sortable: true, - render: (_, { serviceName, agentName }) => ( - - - - {agentName && ( - - - - )} - - - {formatString(serviceName)} - - - - - - ), - }, - { - field: 'environments', - name: i18n.translate('xpack.apm.servicesTable.environmentColumnLabel', { - defaultMessage: 'Environment', - }), - width: px(unit * 10), - sortable: true, - render: (_, { environments }) => ( - - ), - }, - { - field: 'avgResponseTime', - name: i18n.translate('xpack.apm.servicesTable.avgResponseTimeColumnLabel', { - defaultMessage: 'Avg. response time', - }), - sortable: true, - dataType: 'number', - render: (_, { avgResponseTime }) => ( - - ), - align: 'left', - width: px(unit * 10), - }, - { - field: 'transactionsPerMinute', - name: i18n.translate( - 'xpack.apm.servicesTable.transactionsPerMinuteColumnLabel', - { - defaultMessage: 'Trans. per minute', - } - ), - sortable: true, - dataType: 'number', - render: (_, { transactionsPerMinute }) => ( - - ), - align: 'left', - width: px(unit * 10), - }, - { - field: 'transactionErrorRate', - name: i18n.translate('xpack.apm.servicesTable.transactionErrorRate', { - defaultMessage: 'Error rate %', - }), - sortable: true, - dataType: 'number', - render: (_, { transactionErrorRate }) => { - const value = transactionErrorRate?.value; - - const valueLabel = asPercent(value, 1); - - return ( - - ); - }, - align: 'left', - width: px(unit * 10), - }, -]; - const SERVICE_HEALTH_STATUS_ORDER = [ ServiceHealthStatus.unknown, ServiceHealthStatus.healthy, @@ -182,59 +68,249 @@ const SERVICE_HEALTH_STATUS_ORDER = [ ServiceHealthStatus.critical, ]; +export function getServiceColumns({ + showTransactionTypeColumn, +}: { + showTransactionTypeColumn: boolean; +}): Array> { + return [ + { + field: 'healthStatus', + name: i18n.translate('xpack.apm.servicesTable.healthColumnLabel', { + defaultMessage: 'Health', + }), + width: px(unit * 6), + sortable: true, + render: (_, { healthStatus }) => { + return ( + + ); + }, + }, + { + field: 'serviceName', + name: i18n.translate('xpack.apm.servicesTable.nameColumnLabel', { + defaultMessage: 'Name', + }), + width: '40%', + sortable: true, + render: (_, { serviceName, agentName }) => ( + + + + {agentName && ( + + + + )} + + + {formatString(serviceName)} + + + + + + ), + }, + { + field: 'environments', + name: i18n.translate('xpack.apm.servicesTable.environmentColumnLabel', { + defaultMessage: 'Environment', + }), + width: px(unit * 10), + sortable: true, + render: (_, { environments }) => ( + + ), + }, + ...(showTransactionTypeColumn + ? [ + { + field: 'transactionType', + name: i18n.translate( + 'xpack.apm.servicesTable.transactionColumnLabel', + { + defaultMessage: 'Transaction type', + } + ), + width: px(unit * 10), + sortable: true, + }, + ] + : []), + { + field: 'avgResponseTime', + name: i18n.translate( + 'xpack.apm.servicesTable.avgResponseTimeColumnLabel', + { + defaultMessage: 'Avg. response time', + } + ), + sortable: true, + dataType: 'number', + render: (_, { avgResponseTime }) => ( + + ), + align: 'left', + width: px(unit * 10), + }, + { + field: 'transactionsPerMinute', + name: i18n.translate( + 'xpack.apm.servicesTable.transactionsPerMinuteColumnLabel', + { + defaultMessage: 'Trans. per minute', + } + ), + sortable: true, + dataType: 'number', + render: (_, { transactionsPerMinute }) => ( + + ), + align: 'left', + width: px(unit * 10), + }, + { + field: 'transactionErrorRate', + name: i18n.translate('xpack.apm.servicesTable.transactionErrorRate', { + defaultMessage: 'Error rate %', + }), + sortable: true, + dataType: 'number', + render: (_, { transactionErrorRate }) => { + const value = transactionErrorRate?.value; + + const valueLabel = asPercent(value, 1); + + return ( + + ); + }, + align: 'left', + width: px(unit * 10), + }, + ]; +} + export function ServiceList({ items, noItemsMessage }: Props) { const displayHealthStatus = items.some((item) => 'healthStatus' in item); + const showTransactionTypeColumn = items.some( + ({ transactionType }) => + transactionType !== TRANSACTION_REQUEST && + transactionType !== TRANSACTION_PAGE_LOAD + ); + + const serviceColumns = useMemo( + () => getServiceColumns({ showTransactionTypeColumn }), + [showTransactionTypeColumn] + ); + const columns = displayHealthStatus - ? SERVICE_COLUMNS - : SERVICE_COLUMNS.filter((column) => column.field !== 'healthStatus'); + ? serviceColumns + : serviceColumns.filter((column) => column.field !== 'healthStatus'); const initialSortField = displayHealthStatus ? 'healthStatus' : 'transactionsPerMinute'; return ( - { - // For healthStatus, sort items by healthStatus first, then by TPM - return sortField === 'healthStatus' - ? orderBy( - itemsToSort, - [ - (item) => { - return item.healthStatus - ? SERVICE_HEALTH_STATUS_ORDER.indexOf(item.healthStatus) - : -1; - }, - (item) => item.transactionsPerMinute?.value ?? 0, - ], - [sortDirection, sortDirection] - ) - : orderBy( - itemsToSort, - (item) => { - switch (sortField) { - // Use `?? -1` here so `undefined` will appear after/before `0`. - // In the table this will make the "N/A" items always at the - // bottom/top. - case 'avgResponseTime': - return item.avgResponseTime?.value ?? -1; - case 'transactionsPerMinute': - return item.transactionsPerMinute?.value ?? -1; - case 'transactionErrorRate': - return item.transactionErrorRate?.value ?? -1; - default: - return item[sortField as keyof typeof item]; + + + + + + )} + > + + + + + + {i18n.translate( + 'xpack.apm.servicesTable.metricsExplanationLabel', + { defaultMessage: 'What are these metrics?' } + )} + + + + + + { + // For healthStatus, sort items by healthStatus first, then by TPM + return sortField === 'healthStatus' + ? orderBy( + itemsToSort, + [ + (item) => { + return item.healthStatus + ? SERVICE_HEALTH_STATUS_ORDER.indexOf(item.healthStatus) + : -1; + }, + (item) => item.transactionsPerMinute?.value ?? 0, + ], + [sortDirection, sortDirection] + ) + : orderBy( + itemsToSort, + (item) => { + switch (sortField) { + // Use `?? -1` here so `undefined` will appear after/before `0`. + // In the table this will make the "N/A" items always at the + // bottom/top. + case 'avgResponseTime': + return item.avgResponseTime?.value ?? -1; + case 'transactionsPerMinute': + return item.transactionsPerMinute?.value ?? -1; + case 'transactionErrorRate': + return item.transactionErrorRate?.value ?? -1; + default: + return item[sortField as keyof typeof item]; + } + }, + sortDirection + ); + }} + /> + + ); } diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/service_list.test.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/service_list.test.tsx index 1c6fa9fe0447e..45a4afeb53235 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/service_list.test.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/service_list.test.tsx @@ -9,11 +9,8 @@ import { MemoryRouter } from 'react-router-dom'; import { ServiceHealthStatus } from '../../../../../common/service_health_status'; import { MockApmPluginContextWrapper } from '../../../../context/apm_plugin/mock_apm_plugin_context'; import { mockMoment, renderWithTheme } from '../../../../utils/testHelpers'; -import { APIReturnType } from '../../../../services/rest/createCallApmApi'; -import { ServiceList, SERVICE_COLUMNS } from './'; -import props from './__fixtures__/props.json'; - -type ServiceListAPIResponse = APIReturnType<'GET /api/apm/services'>; +import { getServiceColumns, ServiceList } from './'; +import { items } from './__fixtures__/service_api_mock_data'; function Wrapper({ children }: { children?: ReactNode }) { return ( @@ -36,10 +33,7 @@ describe('ServiceList', () => { it('renders with data', () => { expect(() => - renderWithTheme( - , - { wrapper: Wrapper } - ) + renderWithTheme(, { wrapper: Wrapper }) ).not.toThrowError(); }); @@ -61,9 +55,9 @@ describe('ServiceList', () => { }, environments: ['test'], }; - const renderedColumns = SERVICE_COLUMNS.map((c) => - c.render!(service[c.field!], service) - ); + const renderedColumns = getServiceColumns({ + showTransactionTypeColumn: false, + }).map((c) => c.render!(service[c.field!], service)); expect(renderedColumns[0]).toMatchInlineSnapshot(` { describe('without ML data', () => { it('does not render the health column', () => { - const { queryByText } = renderWithTheme( - , - { - wrapper: Wrapper, - } - ); + const { queryByText } = renderWithTheme(, { + wrapper: Wrapper, + }); const healthHeading = queryByText('Health'); expect(healthHeading).toBeNull(); }); it('sorts by transactions per minute', async () => { - const { findByTitle } = renderWithTheme( - , - { - wrapper: Wrapper, - } - ); + const { findByTitle } = renderWithTheme(, { + wrapper: Wrapper, + }); expect( await findByTitle('Trans. per minute; Sorted in descending order') @@ -103,12 +91,10 @@ describe('ServiceList', () => { it('renders the health column', async () => { const { findByTitle } = renderWithTheme( ({ - ...item, - healthStatus: ServiceHealthStatus.warning, - }) - )} + items={items.map((item) => ({ + ...item, + healthStatus: ServiceHealthStatus.warning, + }))} />, { wrapper: Wrapper } ); diff --git a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLExplorerLink.test.tsx b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLExplorerLink.test.tsx new file mode 100644 index 0000000000000..3f02ed082f564 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLExplorerLink.test.tsx @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Location } from 'history'; +import React from 'react'; +import { getRenderedHref } from '../../../../utils/testHelpers'; +import { MLExplorerLink } from './MLExplorerLink'; + +describe('MLExplorerLink', () => { + it('should produce the correct URL with jobId', async () => { + const href = await getRenderedHref( + () => ( + + ), + { + search: + '?rangeFrom=now/w&rangeTo=now-4h&refreshPaused=true&refreshInterval=0', + } as Location + ); + + expect(href).toMatchInlineSnapshot( + `"/app/ml/explorer?_g=(ml:(jobIds:!(myservicename-mytransactiontype-high_mean_response_time)),refreshInterval:(pause:!t,value:0),time:(from:now%2Fw,to:now-4h))&_a=(explorer:(mlExplorerFilter:(),mlExplorerSwimlane:()))"` + ); + }); + + it('correctly encodes time range values', async () => { + const href = await getRenderedHref( + () => ( + + ), + { + search: + '?rangeFrom=2020-07-29T17:27:29.000Z&rangeTo=2020-07-29T18:45:00.000Z&refreshInterval=10000&refreshPaused=true', + } as Location + ); + + expect(href).toMatchInlineSnapshot( + `"/app/ml/explorer?_g=(ml:(jobIds:!(apm-production-485b-high_mean_transaction_duration)),refreshInterval:(pause:!t,value:10000),time:(from:'2020-07-29T17:27:29.000Z',to:'2020-07-29T18:45:00.000Z'))&_a=(explorer:(mlExplorerFilter:(),mlExplorerSwimlane:()))"` + ); + }); +}); diff --git a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLExplorerLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLExplorerLink.tsx new file mode 100644 index 0000000000000..ca9eb063bd090 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLExplorerLink.tsx @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { ReactNode } from 'react'; +import { EuiLink } from '@elastic/eui'; +import { UI_SETTINGS } from '../../../../../../../../src/plugins/data/common'; +import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; +import { useMlHref, ML_PAGES } from '../../../../../../ml/public'; +import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; +import { TimePickerRefreshInterval } from '../../DatePicker/typings'; + +interface Props { + children?: ReactNode; + jobId: string; + external?: boolean; +} + +export function MLExplorerLink({ jobId, external, children }: Props) { + const href = useExplorerHref({ jobId }); + + return ( + + ); +} + +export function useExplorerHref({ jobId }: { jobId: string }) { + const { + core, + plugins: { ml }, + } = useApmPluginContext(); + const { urlParams } = useUrlParams(); + + const timePickerRefreshIntervalDefaults = core.uiSettings.get( + UI_SETTINGS.TIMEPICKER_REFRESH_INTERVAL_DEFAULTS + ); + + const { + // hardcoding a custom default of 1 hour since the default kibana timerange of 15 minutes is shorter than the ML interval + rangeFrom = 'now-1h', + rangeTo = 'now', + refreshInterval = timePickerRefreshIntervalDefaults.value, + refreshPaused = timePickerRefreshIntervalDefaults.pause, + } = urlParams; + + const href = useMlHref(ml, core.http.basePath.get(), { + page: ML_PAGES.ANOMALY_EXPLORER, + pageState: { + jobIds: [jobId], + timeRange: { from: rangeFrom, to: rangeTo }, + refreshInterval: { pause: refreshPaused, value: refreshInterval }, + }, + }); + + return href; +} diff --git a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx index 3f74b80bab064..312513db80886 100644 --- a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx +++ b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx @@ -63,7 +63,13 @@ export function TransactionActionMenu({ transaction }: Props) { isOpen={isActionPopoverOpen} anchorPosition="downRight" button={ - setIsActionPopoverOpen(true)} /> + + setIsActionPopoverOpen( + (prevIsActionPopoverOpen) => !prevIsActionPopoverOpen + ) + } + /> } >
diff --git a/x-pack/plugins/apm/public/components/shared/charts/transaction_charts/ml_header.tsx b/x-pack/plugins/apm/public/components/shared/charts/transaction_charts/ml_header.tsx index d125af70268cb..33dcbf02ccda7 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/transaction_charts/ml_header.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/transaction_charts/ml_header.tsx @@ -10,6 +10,7 @@ import { isEmpty } from 'lodash'; import React from 'react'; import { useParams } from 'react-router-dom'; import styled from 'styled-components'; +import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context'; import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; import { MLSingleMetricLink } from '../../Links/MachineLearningLinks/MLSingleMetricLink'; @@ -33,12 +34,13 @@ const ShiftedEuiText = styled(EuiText)` export function MLHeader({ hasValidMlLicense, mlJobId }: Props) { const { serviceName } = useParams<{ serviceName?: string }>(); const { urlParams } = useUrlParams(); + const { transactionType } = useApmServiceContext(); if (!hasValidMlLicense || !mlJobId) { return null; } - const { kuery, transactionType } = urlParams; + const { kuery } = urlParams; const hasKuery = !isEmpty(kuery); const icon = hasKuery ? ( diff --git a/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.test.ts b/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.test.ts index ac85142f3050b..03877b9e5bff2 100644 --- a/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.test.ts +++ b/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.test.ts @@ -37,6 +37,14 @@ describe('getThrouputChartSelector', () => { expect(throughputTimeseries).toEqual({ throughputTimeseries: [] }); }); + it('returns default values when timeseries is empty', () => { + const throughputTimeseries = getThrouputChartSelector({ + theme, + throuputChart: { throughputTimeseries: [] }, + }); + expect(throughputTimeseries).toEqual({ throughputTimeseries: [] }); + }); + it('return throughput time series', () => { const throughputTimeseries = getThrouputChartSelector({ theme, diff --git a/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.ts b/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.ts index 701558b154677..a392f247aec42 100644 --- a/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.ts +++ b/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.ts @@ -8,7 +8,6 @@ import { difference, zipObject } from 'lodash'; import { EuiTheme } from '../../../observability/public'; import { asTransactionRate } from '../../common/utils/formatters'; import { TimeSeries } from '../../typings/timeseries'; -import { getEmptySeries } from '../components/shared/charts/helper/get_empty_series'; import { APIReturnType } from '../services/rest/createCallApmApi'; import { httpStatusCodeToColor } from '../utils/httpStatusCodeToColor'; @@ -34,7 +33,7 @@ export function getThrouputChartSelector({ }; } -export function getThroughputTimeseries({ +function getThroughputTimeseries({ throuputChart, theme, }: { @@ -45,15 +44,6 @@ export function getThroughputTimeseries({ const bucketKeys = throughputTimeseries.map(({ key }) => key); const getColor = getColorByKey(bucketKeys, theme); - if (!throughputTimeseries.length) { - const start = throughputTimeseries[0].dataPoints[0].x; - const end = - throughputTimeseries[0].dataPoints[ - throughputTimeseries[0].dataPoints.length - 1 - ].x; - return getEmptySeries(start, end); - } - return throughputTimeseries.map((bucket) => { return { title: bucket.key, diff --git a/x-pack/plugins/apm/server/lib/helpers/transaction_error_rate.ts b/x-pack/plugins/apm/server/lib/helpers/transaction_error_rate.ts index 536be56e152a3..876fc6b822213 100644 --- a/x-pack/plugins/apm/server/lib/helpers/transaction_error_rate.ts +++ b/x-pack/plugins/apm/server/lib/helpers/transaction_error_rate.ts @@ -18,7 +18,10 @@ export function getOutcomeAggregation({ searchAggregatedTransactions: boolean; }) { return { - terms: { field: EVENT_OUTCOME }, + terms: { + field: EVENT_OUTCOME, + include: [EventOutcome.failure, EventOutcome.success], + }, aggs: { // simply using the doc count to get the number of requests is not possible for transaction metrics (histograms) // to work around this we get the number of transactions by counting the number of latency values diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_web_core_vitals.ts b/x-pack/plugins/apm/server/lib/rum_client/get_web_core_vitals.ts index 76a718bbb2a02..68bdc4b3d0ae8 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/get_web_core_vitals.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/get_web_core_vitals.ts @@ -132,7 +132,9 @@ export async function getWebCoreVitals({ return { coreVitalPages: coreVitalPages?.doc_count ?? 0, - cls: cls?.values[pkey]?.toFixed(3) || null, + /* Because cls is required in the type UXMetrics, and defined as number | null, + * we need to default to null in the case where cls is undefined in order to satisfy the UXMetrics type */ + cls: cls?.values[pkey] ?? null, fid: fid?.values[pkey], lcp: lcp?.values[pkey], tbt: tbt?.values[pkey] ?? 0, diff --git a/x-pack/plugins/apm/server/lib/service_map/fetch_service_paths_from_trace_ids.ts b/x-pack/plugins/apm/server/lib/service_map/fetch_service_paths_from_trace_ids.ts index 14047f4bacea9..f40ae7803e364 100644 --- a/x-pack/plugins/apm/server/lib/service_map/fetch_service_paths_from_trace_ids.ts +++ b/x-pack/plugins/apm/server/lib/service_map/fetch_service_paths_from_trace_ids.ts @@ -3,6 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import { rangeFilter } from '../../../common/utils/range_filter'; import { ProcessorEvent } from '../../../common/processor_event'; import { TRACE_ID } from '../../../common/elasticsearch_fieldnames'; import { @@ -10,14 +11,19 @@ import { ExternalConnectionNode, ServiceConnectionNode, } from '../../../common/service_map'; -import { Setup } from '../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../helpers/setup_request'; export async function fetchServicePathsFromTraceIds( - setup: Setup, + setup: Setup & SetupTimeRange, traceIds: string[] ) { const { apmEventClient } = setup; + // make sure there's a range so ES can skip shards + const dayInMs = 24 * 60 * 60 * 1000; + const start = setup.start - dayInMs; + const end = setup.end + dayInMs; + const serviceMapParams = { apm: { events: [ProcessorEvent.span, ProcessorEvent.transaction], @@ -32,6 +38,7 @@ export async function fetchServicePathsFromTraceIds( [TRACE_ID]: traceIds, }, }, + { range: rangeFilter(start, end) }, ], }, }, diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map_from_trace_ids.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map_from_trace_ids.ts index 14cfece22d053..b650602062c0b 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_service_map_from_trace_ids.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map_from_trace_ids.ts @@ -10,7 +10,7 @@ import { SERVICE_NAME, } from '../../../common/elasticsearch_fieldnames'; import { Connection, ConnectionNode } from '../../../common/service_map'; -import { Setup } from '../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { fetchServicePathsFromTraceIds } from './fetch_service_paths_from_trace_ids'; export function getConnections({ @@ -79,7 +79,7 @@ export async function getServiceMapFromTraceIds({ serviceName, environment, }: { - setup: Setup; + setup: Setup & SetupTimeRange; traceIds: string[]; serviceName?: string; environment?: string; diff --git a/x-pack/plugins/apm/server/lib/service_nodes/index.ts b/x-pack/plugins/apm/server/lib/service_nodes/index.ts index d5e29532e3d7b..ca58a1b0e7126 100644 --- a/x-pack/plugins/apm/server/lib/service_nodes/index.ts +++ b/x-pack/plugins/apm/server/lib/service_nodes/index.ts @@ -4,16 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Setup, SetupTimeRange } from '../helpers/setup_request'; -import { getServiceNodesProjection } from '../../projections/service_nodes'; -import { mergeProjection } from '../../projections/util/merge_projection'; -import { SERVICE_NODE_NAME_MISSING } from '../../../common/service_nodes'; import { - METRIC_PROCESS_CPU_PERCENT, - METRIC_JAVA_THREAD_COUNT, METRIC_JAVA_HEAP_MEMORY_USED, METRIC_JAVA_NON_HEAP_MEMORY_USED, + METRIC_JAVA_THREAD_COUNT, + METRIC_PROCESS_CPU_PERCENT, } from '../../../common/elasticsearch_fieldnames'; +import { SERVICE_NODE_NAME_MISSING } from '../../../common/service_nodes'; +import { getServiceNodesProjection } from '../../projections/service_nodes'; +import { mergeProjection } from '../../projections/util/merge_projection'; +import { Setup, SetupTimeRange } from '../helpers/setup_request'; const getServiceNodes = async ({ setup, @@ -68,15 +68,21 @@ const getServiceNodes = async ({ return []; } - return response.aggregations.nodes.buckets.map((bucket) => { - return { + return response.aggregations.nodes.buckets + .map((bucket) => ({ name: bucket.key as string, cpu: bucket.cpu.value, heapMemory: bucket.heapMemory.value, nonHeapMemory: bucket.nonHeapMemory.value, threadCount: bucket.threadCount.value, - }; - }); + })) + .filter( + (item) => + item.cpu !== null || + item.heapMemory !== null || + item.nonHeapMemory !== null || + item.threadCount != null + ); }; export { getServiceNodes }; diff --git a/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap index a6818f96c728e..21402e4c8dac0 100644 --- a/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap @@ -100,196 +100,27 @@ Array [ "aggs": Object { "services": Object { "aggs": Object { - "average": Object { - "avg": Object { - "field": "transaction.duration.us", - }, - }, - "timeseries": Object { + "transactionType": Object { "aggs": Object { - "average": Object { - "avg": Object { - "field": "transaction.duration.us", + "agentName": Object { + "top_hits": Object { + "docvalue_fields": Array [ + "agent.name", + ], + "size": 1, }, }, - }, - "date_histogram": Object { - "extended_bounds": Object { - "max": 1528977600000, - "min": 1528113600000, - }, - "field": "@timestamp", - "fixed_interval": "43200s", - "min_doc_count": 0, - }, - }, - }, - "terms": Object { - "field": "service.name", - "size": 500, - }, - }, - }, - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "range": Object { - "@timestamp": Object { - "format": "epoch_millis", - "gte": 1528113600000, - "lte": 1528977600000, - }, - }, - }, - Object { - "term": Object { - "service.environment": "test", - }, - }, - ], - }, - }, - "size": 0, - }, - }, - Object { - "apm": Object { - "events": Array [ - "transaction", - "metric", - "error", - ], - }, - "body": Object { - "aggs": Object { - "services": Object { - "aggs": Object { - "agent_name": Object { - "top_hits": Object { - "_source": Array [ - "agent.name", - ], - "size": 1, - }, - }, - }, - "terms": Object { - "field": "service.name", - "size": 500, - }, - }, - }, - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "range": Object { - "@timestamp": Object { - "format": "epoch_millis", - "gte": 1528113600000, - "lte": 1528977600000, - }, - }, - }, - Object { - "term": Object { - "service.environment": "test", - }, - }, - ], - }, - }, - "size": 0, - }, - }, - Object { - "apm": Object { - "events": Array [ - "transaction", - ], - }, - "body": Object { - "aggs": Object { - "services": Object { - "aggs": Object { - "count": Object { - "value_count": Object { - "field": "transaction.duration.us", - }, - }, - "timeseries": Object { - "aggs": Object { - "count": Object { - "value_count": Object { + "avg_duration": Object { + "avg": Object { "field": "transaction.duration.us", }, }, - }, - "date_histogram": Object { - "extended_bounds": Object { - "max": 1528977600000, - "min": 1528113600000, - }, - "field": "@timestamp", - "fixed_interval": "43200s", - "min_doc_count": 0, - }, - }, - }, - "terms": Object { - "field": "service.name", - "size": 500, - }, - }, - }, - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "range": Object { - "@timestamp": Object { - "format": "epoch_millis", - "gte": 1528113600000, - "lte": 1528977600000, - }, - }, - }, - Object { - "term": Object { - "service.environment": "test", - }, - }, - ], - }, - }, - "size": 0, - }, - }, - Object { - "apm": Object { - "events": Array [ - "transaction", - ], - }, - "body": Object { - "aggs": Object { - "services": Object { - "aggs": Object { - "outcomes": Object { - "aggs": Object { - "count": Object { - "value_count": Object { - "field": "transaction.duration.us", + "environments": Object { + "terms": Object { + "field": "service.environment", + "missing": "", }, }, - }, - "terms": Object { - "field": "event.outcome", - }, - }, - "timeseries": Object { - "aggs": Object { "outcomes": Object { "aggs": Object { "count": Object { @@ -300,73 +131,62 @@ Array [ }, "terms": Object { "field": "event.outcome", + "include": Array [ + "failure", + "success", + ], }, }, - }, - "date_histogram": Object { - "extended_bounds": Object { - "max": 1528977600000, - "min": 1528113600000, + "real_document_count": Object { + "value_count": Object { + "field": "transaction.duration.us", + }, }, - "field": "@timestamp", - "fixed_interval": "43200s", - "min_doc_count": 0, - }, - }, - }, - "terms": Object { - "field": "service.name", - "size": 500, - }, - }, - }, - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "range": Object { - "@timestamp": Object { - "format": "epoch_millis", - "gte": 1528113600000, - "lte": 1528977600000, + "timeseries": Object { + "aggs": Object { + "avg_duration": Object { + "avg": Object { + "field": "transaction.duration.us", + }, + }, + "outcomes": Object { + "aggs": Object { + "count": Object { + "value_count": Object { + "field": "transaction.duration.us", + }, + }, + }, + "terms": Object { + "field": "event.outcome", + "include": Array [ + "failure", + "success", + ], + }, + }, + "real_document_count": Object { + "value_count": Object { + "field": "transaction.duration.us", + }, + }, + }, + "date_histogram": Object { + "extended_bounds": Object { + "max": 1528977600000, + "min": 1528113600000, + }, + "field": "@timestamp", + "fixed_interval": "43200s", + "min_doc_count": 0, + }, }, }, - }, - Object { - "term": Object { - "service.environment": "test", - }, - }, - Object { - "terms": Object { - "event.outcome": Array [ - "failure", - "success", - ], - }, - }, - ], - }, - }, - "size": 0, - }, - }, - Object { - "apm": Object { - "events": Array [ - "transaction", - "metric", - "error", - ], - }, - "body": Object { - "aggs": Object { - "services": Object { - "aggs": Object { - "environments": Object { "terms": Object { - "field": "service.environment", - "size": 100, + "field": "transaction.type", + "order": Object { + "real_document_count": "desc", + }, }, }, }, diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_health_statuses.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_health_statuses.ts new file mode 100644 index 0000000000000..206827a744113 --- /dev/null +++ b/x-pack/plugins/apm/server/lib/services/get_services/get_health_statuses.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { getSeverity } from '../../../../common/anomaly_detection'; +import { getServiceHealthStatus } from '../../../../common/service_health_status'; +import { + getMLJobIds, + getServiceAnomalies, +} from '../../service_map/get_service_anomalies'; +import { + ServicesItemsProjection, + ServicesItemsSetup, +} from './get_services_items'; + +interface AggregationParams { + setup: ServicesItemsSetup; + projection: ServicesItemsProjection; + searchAggregatedTransactions: boolean; +} + +export const getHealthStatuses = async ( + { setup }: AggregationParams, + mlAnomaliesEnvironment?: string +) => { + if (!setup.ml) { + return []; + } + + const jobIds = await getMLJobIds( + setup.ml.anomalyDetectors, + mlAnomaliesEnvironment + ); + if (!jobIds.length) { + return []; + } + + const anomalies = await getServiceAnomalies({ + setup, + environment: mlAnomaliesEnvironment, + }); + + return Object.keys(anomalies.serviceAnomalies).map((serviceName) => { + const stats = anomalies.serviceAnomalies[serviceName]; + + const severity = getSeverity(stats.anomalyScore); + const healthStatus = getServiceHealthStatus({ severity }); + + return { + serviceName, + healthStatus, + }; + }); +}; diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_service_transaction_stats.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_service_transaction_stats.ts new file mode 100644 index 0000000000000..0ee7080dc0834 --- /dev/null +++ b/x-pack/plugins/apm/server/lib/services/get_services/get_service_transaction_stats.ts @@ -0,0 +1,199 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + AGENT_NAME, + SERVICE_ENVIRONMENT, + SERVICE_NAME, + TRANSACTION_TYPE, +} from '../../../../common/elasticsearch_fieldnames'; +import { + TRANSACTION_PAGE_LOAD, + TRANSACTION_REQUEST, +} from '../../../../common/transaction_types'; +import { rangeFilter } from '../../../../common/utils/range_filter'; +import { AgentName } from '../../../../typings/es_schemas/ui/fields/agent'; +import { + getDocumentTypeFilterForAggregatedTransactions, + getProcessorEventForAggregatedTransactions, + getTransactionDurationFieldForAggregatedTransactions, +} from '../../helpers/aggregated_transactions'; +import { getBucketSize } from '../../helpers/get_bucket_size'; +import { + calculateTransactionErrorPercentage, + getOutcomeAggregation, +} from '../../helpers/transaction_error_rate'; +import { ServicesItemsSetup } from './get_services_items'; + +interface AggregationParams { + setup: ServicesItemsSetup; + searchAggregatedTransactions: boolean; +} + +const MAX_NUMBER_OF_SERVICES = 500; + +function calculateAvgDuration({ + value, + deltaAsMinutes, +}: { + value: number; + deltaAsMinutes: number; +}) { + return value / deltaAsMinutes; +} + +export async function getServiceTransactionStats({ + setup, + searchAggregatedTransactions, +}: AggregationParams) { + const { apmEventClient, start, end, esFilter } = setup; + + const outcomes = getOutcomeAggregation({ searchAggregatedTransactions }); + + const metrics = { + real_document_count: { + value_count: { + field: getTransactionDurationFieldForAggregatedTransactions( + searchAggregatedTransactions + ), + }, + }, + avg_duration: { + avg: { + field: getTransactionDurationFieldForAggregatedTransactions( + searchAggregatedTransactions + ), + }, + }, + outcomes, + }; + + const response = await apmEventClient.search({ + apm: { + events: [ + getProcessorEventForAggregatedTransactions( + searchAggregatedTransactions + ), + ], + }, + body: { + size: 0, + query: { + bool: { + filter: [ + { range: rangeFilter(start, end) }, + ...esFilter, + ...getDocumentTypeFilterForAggregatedTransactions( + searchAggregatedTransactions + ), + ], + }, + }, + aggs: { + services: { + terms: { + field: SERVICE_NAME, + size: MAX_NUMBER_OF_SERVICES, + }, + aggs: { + transactionType: { + terms: { + field: TRANSACTION_TYPE, + order: { real_document_count: 'desc' }, + }, + aggs: { + ...metrics, + environments: { + terms: { + field: SERVICE_ENVIRONMENT, + missing: '', + }, + }, + agentName: { + top_hits: { + docvalue_fields: [AGENT_NAME] as const, + size: 1, + }, + }, + timeseries: { + date_histogram: { + field: '@timestamp', + fixed_interval: getBucketSize({ + start, + end, + numBuckets: 20, + }).intervalString, + min_doc_count: 0, + extended_bounds: { min: start, max: end }, + }, + aggs: metrics, + }, + }, + }, + }, + }, + }, + }, + }); + + const deltaAsMinutes = (setup.end - setup.start) / 1000 / 60; + + return ( + response.aggregations?.services.buckets.map((bucket) => { + const topTransactionTypeBucket = + bucket.transactionType.buckets.find( + ({ key }) => + key === TRANSACTION_REQUEST || key === TRANSACTION_PAGE_LOAD + ) ?? bucket.transactionType.buckets[0]; + + return { + serviceName: bucket.key as string, + transactionType: topTransactionTypeBucket.key as string, + environments: topTransactionTypeBucket.environments.buckets + .map((environmentBucket) => environmentBucket.key as string) + .filter(Boolean), + agentName: topTransactionTypeBucket.agentName.hits.hits[0].fields[ + 'agent.name' + ]?.[0] as AgentName, + avgResponseTime: { + value: topTransactionTypeBucket.avg_duration.value, + timeseries: topTransactionTypeBucket.timeseries.buckets.map( + (dateBucket) => ({ + x: dateBucket.key, + y: dateBucket.avg_duration.value, + }) + ), + }, + transactionErrorRate: { + value: calculateTransactionErrorPercentage( + topTransactionTypeBucket.outcomes + ), + timeseries: topTransactionTypeBucket.timeseries.buckets.map( + (dateBucket) => ({ + x: dateBucket.key, + y: calculateTransactionErrorPercentage(dateBucket.outcomes), + }) + ), + }, + transactionsPerMinute: { + value: calculateAvgDuration({ + value: topTransactionTypeBucket.real_document_count.value, + deltaAsMinutes, + }), + timeseries: topTransactionTypeBucket.timeseries.buckets.map( + (dateBucket) => ({ + x: dateBucket.key, + y: calculateAvgDuration({ + value: dateBucket.real_document_count.value, + deltaAsMinutes, + }), + }) + ), + }, + }; + }) ?? [] + ); +} diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts index 11f3e44fce87c..359c677b00baf 100644 --- a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts +++ b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts @@ -7,14 +7,8 @@ import { Logger } from '@kbn/logging'; import { joinByKey } from '../../../../common/utils/join_by_key'; import { getServicesProjection } from '../../../projections/services'; import { Setup, SetupTimeRange } from '../../helpers/setup_request'; -import { - getAgentNames, - getEnvironments, - getHealthStatuses, - getTransactionDurationAverages, - getTransactionErrorRates, - getTransactionRates, -} from './get_services_items_stats'; +import { getHealthStatuses } from './get_health_statuses'; +import { getServiceTransactionStats } from './get_service_transaction_stats'; export type ServicesItemsSetup = Setup & SetupTimeRange; export type ServicesItemsProjection = ReturnType; @@ -37,46 +31,23 @@ export async function getServicesItems({ searchAggregatedTransactions, }; - const [ - transactionDurationAverages, - agentNames, - transactionRates, - transactionErrorRates, - environments, - healthStatuses, - ] = await Promise.all([ - getTransactionDurationAverages(params), - getAgentNames(params), - getTransactionRates(params), - getTransactionErrorRates(params), - getEnvironments(params), + const [transactionStats, healthStatuses] = await Promise.all([ + getServiceTransactionStats(params), getHealthStatuses(params, setup.uiFilters.environment).catch((err) => { logger.error(err); return []; }), ]); - const apmServiceMetrics = joinByKey( - [ - ...transactionDurationAverages, - ...agentNames, - ...transactionRates, - ...transactionErrorRates, - ...environments, - ], - 'serviceName' - ); - - const apmServices = apmServiceMetrics.map(({ serviceName }) => serviceName); + const apmServices = transactionStats.map(({ serviceName }) => serviceName); // make sure to exclude health statuses from services // that are not found in APM data - const matchedHealthStatuses = healthStatuses.filter(({ serviceName }) => apmServices.includes(serviceName) ); - const allMetrics = [...apmServiceMetrics, ...matchedHealthStatuses]; + const allMetrics = [...transactionStats, ...matchedHealthStatuses]; return joinByKey(allMetrics, 'serviceName'); } diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items_stats.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items_stats.ts deleted file mode 100644 index c8ebaa13d9df9..0000000000000 --- a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items_stats.ts +++ /dev/null @@ -1,413 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { getServiceHealthStatus } from '../../../../common/service_health_status'; -import { EventOutcome } from '../../../../common/event_outcome'; -import { getSeverity } from '../../../../common/anomaly_detection'; -import { AgentName } from '../../../../typings/es_schemas/ui/fields/agent'; -import { - AGENT_NAME, - SERVICE_ENVIRONMENT, - EVENT_OUTCOME, -} from '../../../../common/elasticsearch_fieldnames'; -import { mergeProjection } from '../../../projections/util/merge_projection'; -import { - ServicesItemsSetup, - ServicesItemsProjection, -} from './get_services_items'; -import { - getDocumentTypeFilterForAggregatedTransactions, - getProcessorEventForAggregatedTransactions, - getTransactionDurationFieldForAggregatedTransactions, -} from '../../helpers/aggregated_transactions'; -import { getBucketSize } from '../../helpers/get_bucket_size'; -import { - getMLJobIds, - getServiceAnomalies, -} from '../../service_map/get_service_anomalies'; -import { - calculateTransactionErrorPercentage, - getOutcomeAggregation, - getTransactionErrorRateTimeSeries, -} from '../../helpers/transaction_error_rate'; - -function getDateHistogramOpts(start: number, end: number) { - return { - field: '@timestamp', - fixed_interval: getBucketSize({ start, end, numBuckets: 20 }) - .intervalString, - min_doc_count: 0, - extended_bounds: { min: start, max: end }, - }; -} - -const MAX_NUMBER_OF_SERVICES = 500; - -const getDeltaAsMinutes = (setup: ServicesItemsSetup) => - (setup.end - setup.start) / 1000 / 60; - -interface AggregationParams { - setup: ServicesItemsSetup; - projection: ServicesItemsProjection; - searchAggregatedTransactions: boolean; -} - -export const getTransactionDurationAverages = async ({ - setup, - projection, - searchAggregatedTransactions, -}: AggregationParams) => { - const { apmEventClient, start, end } = setup; - - const response = await apmEventClient.search( - mergeProjection(projection, { - apm: { - events: [ - getProcessorEventForAggregatedTransactions( - searchAggregatedTransactions - ), - ], - }, - body: { - size: 0, - query: { - bool: { - filter: [ - ...projection.body.query.bool.filter, - ...getDocumentTypeFilterForAggregatedTransactions( - searchAggregatedTransactions - ), - ], - }, - }, - aggs: { - services: { - terms: { - ...projection.body.aggs.services.terms, - size: MAX_NUMBER_OF_SERVICES, - }, - aggs: { - average: { - avg: { - field: getTransactionDurationFieldForAggregatedTransactions( - searchAggregatedTransactions - ), - }, - }, - timeseries: { - date_histogram: getDateHistogramOpts(start, end), - aggs: { - average: { - avg: { - field: getTransactionDurationFieldForAggregatedTransactions( - searchAggregatedTransactions - ), - }, - }, - }, - }, - }, - }, - }, - }, - }) - ); - - const { aggregations } = response; - - if (!aggregations) { - return []; - } - - return aggregations.services.buckets.map((serviceBucket) => ({ - serviceName: serviceBucket.key as string, - avgResponseTime: { - value: serviceBucket.average.value, - timeseries: serviceBucket.timeseries.buckets.map((dateBucket) => ({ - x: dateBucket.key, - y: dateBucket.average.value, - })), - }, - })); -}; - -export const getAgentNames = async ({ - setup, - projection, -}: AggregationParams) => { - const { apmEventClient } = setup; - const response = await apmEventClient.search( - mergeProjection(projection, { - body: { - size: 0, - aggs: { - services: { - terms: { - ...projection.body.aggs.services.terms, - size: MAX_NUMBER_OF_SERVICES, - }, - aggs: { - agent_name: { - top_hits: { - _source: [AGENT_NAME], - size: 1, - }, - }, - }, - }, - }, - }, - }) - ); - - const { aggregations } = response; - - if (!aggregations) { - return []; - } - - return aggregations.services.buckets.map((serviceBucket) => ({ - serviceName: serviceBucket.key as string, - agentName: serviceBucket.agent_name.hits.hits[0]?._source.agent - .name as AgentName, - })); -}; - -export const getTransactionRates = async ({ - setup, - projection, - searchAggregatedTransactions, -}: AggregationParams) => { - const { apmEventClient, start, end } = setup; - const response = await apmEventClient.search( - mergeProjection(projection, { - apm: { - events: [ - getProcessorEventForAggregatedTransactions( - searchAggregatedTransactions - ), - ], - }, - body: { - size: 0, - query: { - bool: { - filter: [ - ...projection.body.query.bool.filter, - ...getDocumentTypeFilterForAggregatedTransactions( - searchAggregatedTransactions - ), - ], - }, - }, - aggs: { - services: { - terms: { - ...projection.body.aggs.services.terms, - size: MAX_NUMBER_OF_SERVICES, - }, - aggs: { - count: { - value_count: { - field: getTransactionDurationFieldForAggregatedTransactions( - searchAggregatedTransactions - ), - }, - }, - timeseries: { - date_histogram: getDateHistogramOpts(start, end), - aggs: { - count: { - value_count: { - field: getTransactionDurationFieldForAggregatedTransactions( - searchAggregatedTransactions - ), - }, - }, - }, - }, - }, - }, - }, - }, - }) - ); - - const { aggregations } = response; - - if (!aggregations) { - return []; - } - - const deltaAsMinutes = getDeltaAsMinutes(setup); - - return aggregations.services.buckets.map((serviceBucket) => { - const transactionsPerMinute = serviceBucket.count.value / deltaAsMinutes; - return { - serviceName: serviceBucket.key as string, - transactionsPerMinute: { - value: transactionsPerMinute, - timeseries: serviceBucket.timeseries.buckets.map((dateBucket) => ({ - x: dateBucket.key, - y: dateBucket.count.value / deltaAsMinutes, - })), - }, - }; - }); -}; - -export const getTransactionErrorRates = async ({ - setup, - projection, - searchAggregatedTransactions, -}: AggregationParams) => { - const { apmEventClient, start, end } = setup; - - const outcomes = getOutcomeAggregation({ searchAggregatedTransactions }); - - const response = await apmEventClient.search( - mergeProjection(projection, { - apm: { - events: [ - getProcessorEventForAggregatedTransactions( - searchAggregatedTransactions - ), - ], - }, - body: { - size: 0, - query: { - bool: { - filter: [ - ...projection.body.query.bool.filter, - { - terms: { - [EVENT_OUTCOME]: [EventOutcome.failure, EventOutcome.success], - }, - }, - ], - }, - }, - aggs: { - services: { - terms: { - ...projection.body.aggs.services.terms, - size: MAX_NUMBER_OF_SERVICES, - }, - aggs: { - outcomes, - timeseries: { - date_histogram: getDateHistogramOpts(start, end), - aggs: { - outcomes, - }, - }, - }, - }, - }, - }, - }) - ); - - const { aggregations } = response; - - if (!aggregations) { - return []; - } - - return aggregations.services.buckets.map((serviceBucket) => { - const transactionErrorRate = calculateTransactionErrorPercentage( - serviceBucket.outcomes - ); - return { - serviceName: serviceBucket.key as string, - transactionErrorRate: { - value: transactionErrorRate, - timeseries: getTransactionErrorRateTimeSeries( - serviceBucket.timeseries.buckets - ), - }, - }; - }); -}; - -export const getEnvironments = async ({ - setup, - projection, -}: AggregationParams) => { - const { apmEventClient, config } = setup; - const maxServiceEnvironments = config['xpack.apm.maxServiceEnvironments']; - const response = await apmEventClient.search( - mergeProjection(projection, { - body: { - size: 0, - aggs: { - services: { - terms: { - ...projection.body.aggs.services.terms, - size: MAX_NUMBER_OF_SERVICES, - }, - aggs: { - environments: { - terms: { - field: SERVICE_ENVIRONMENT, - size: maxServiceEnvironments, - }, - }, - }, - }, - }, - }, - }) - ); - - const { aggregations } = response; - - if (!aggregations) { - return []; - } - - return aggregations.services.buckets.map((serviceBucket) => ({ - serviceName: serviceBucket.key as string, - environments: serviceBucket.environments.buckets.map( - (envBucket) => envBucket.key as string - ), - })); -}; - -export const getHealthStatuses = async ( - { setup }: AggregationParams, - mlAnomaliesEnvironment?: string -) => { - if (!setup.ml) { - return []; - } - - const jobIds = await getMLJobIds( - setup.ml.anomalyDetectors, - mlAnomaliesEnvironment - ); - if (!jobIds.length) { - return []; - } - - const anomalies = await getServiceAnomalies({ - setup, - environment: mlAnomaliesEnvironment, - }); - - return Object.keys(anomalies.serviceAnomalies).map((serviceName) => { - const stats = anomalies.serviceAnomalies[serviceName]; - - const severity = getSeverity(stats.anomalyScore); - const healthStatus = getServiceHealthStatus({ severity }); - - return { - serviceName, - healthStatus, - }; - }); -}; diff --git a/x-pack/plugins/apm/server/routes/settings/custom_link.ts b/x-pack/plugins/apm/server/routes/settings/custom_link.ts index fdf2fe3521d7e..70755540721dd 100644 --- a/x-pack/plugins/apm/server/routes/settings/custom_link.ts +++ b/x-pack/plugins/apm/server/routes/settings/custom_link.ts @@ -64,7 +64,7 @@ export const createCustomLinkRoute = createRoute({ params: t.type({ body: payloadRt, }), - options: { tags: ['access:apm'] }, + options: { tags: ['access:apm', 'access:apm_write'] }, handler: async ({ context, request }) => { if (!isActiveGoldLicense(context.licensing.license)) { throw Boom.forbidden(INVALID_LICENSE); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/__stories__/render.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/__stories__/render.tsx index 54702f2654839..34f4bb39fbfa7 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/__stories__/render.tsx +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/__stories__/render.tsx @@ -12,6 +12,7 @@ export const defaultHandlers: RendererHandlers = { getElementId: () => 'element-id', getFilter: () => 'filter', getRenderMode: () => 'display', + isSyncColorsEnabled: () => false, onComplete: (fn) => undefined, onEmbeddableDestroyed: action('onEmbeddableDestroyed'), onEmbeddableInputChange: action('onEmbeddableInputChange'), diff --git a/x-pack/plugins/canvas/public/components/workpad_templates/examples/__snapshots__/workpad_templates.stories.storyshot b/x-pack/plugins/canvas/public/components/workpad_templates/examples/__snapshots__/workpad_templates.stories.storyshot index 1f7105b80de4c..d267ba07078fe 100644 --- a/x-pack/plugins/canvas/public/components/workpad_templates/examples/__snapshots__/workpad_templates.stories.storyshot +++ b/x-pack/plugins/canvas/public/components/workpad_templates/examples/__snapshots__/workpad_templates.stories.storyshot @@ -178,11 +178,7 @@ exports[`Storyshots components/WorkpadTemplates default 1`] = ` > - } + title="; Sorted in ascending order" > Template name diff --git a/x-pack/plugins/canvas/public/lib/create_handlers.ts b/x-pack/plugins/canvas/public/lib/create_handlers.ts index 9bc4bd5e78fd0..4c9dbd92d3f21 100644 --- a/x-pack/plugins/canvas/public/lib/create_handlers.ts +++ b/x-pack/plugins/canvas/public/lib/create_handlers.ts @@ -26,6 +26,9 @@ export const createHandlers = (): RendererHandlers => ({ getRenderMode() { return 'display'; }, + isSyncColorsEnabled() { + return false; + }, onComplete(fn: () => void) { this.done = fn; }, diff --git a/x-pack/plugins/canvas/server/collectors/collector.ts b/x-pack/plugins/canvas/server/collectors/collector.ts index a084e8fe3349e..9cb015eed4c5b 100644 --- a/x-pack/plugins/canvas/server/collectors/collector.ts +++ b/x-pack/plugins/canvas/server/collectors/collector.ts @@ -37,9 +37,9 @@ export function registerCanvasUsageCollector( const canvasCollector = usageCollection.makeUsageCollector({ type: 'canvas', isReady: () => true, - fetch: async ({ callCluster }: CollectorFetchContext) => { + fetch: async ({ esClient }: CollectorFetchContext) => { const collectorResults = await Promise.all( - collectors.map((collector) => collector(kibanaIndex, callCluster)) + collectors.map((collector) => collector(kibanaIndex, esClient)) ); return collectorResults.reduce((reduction, usage) => { diff --git a/x-pack/plugins/canvas/server/collectors/custom_element_collector.ts b/x-pack/plugins/canvas/server/collectors/custom_element_collector.ts index d3ed1e17785ee..8992f78332518 100644 --- a/x-pack/plugins/canvas/server/collectors/custom_element_collector.ts +++ b/x-pack/plugins/canvas/server/collectors/custom_element_collector.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SearchParams } from 'elasticsearch'; +import { SearchResponse } from 'elasticsearch'; import { get } from 'lodash'; import { MakeSchemaFrom } from 'src/plugins/usage_collection/server'; import { collectFns } from './collector_helpers'; @@ -114,9 +114,9 @@ export function summarizeCustomElements( const customElementCollector: TelemetryCollector = async function customElementCollector( kibanaIndex, - callCluster + esClient ) { - const customElementParams: SearchParams = { + const customElementParams = { size: 10000, index: kibanaIndex, ignoreUnavailable: true, @@ -124,7 +124,9 @@ const customElementCollector: TelemetryCollector = async function customElementC body: { query: { bool: { filter: { term: { type: CUSTOM_ELEMENT_TYPE } } } } }, }; - const esResponse = await callCluster('search', customElementParams); + const { body: esResponse } = await esClient.search>( + customElementParams + ); if (get(esResponse, 'hits.hits.length') > 0) { const customElements = esResponse.hits.hits.map((hit) => hit._source[CUSTOM_ELEMENT_TYPE]); diff --git a/x-pack/plugins/canvas/server/collectors/workpad_collector.ts b/x-pack/plugins/canvas/server/collectors/workpad_collector.ts index 0479411528802..5d633a0d4dd1d 100644 --- a/x-pack/plugins/canvas/server/collectors/workpad_collector.ts +++ b/x-pack/plugins/canvas/server/collectors/workpad_collector.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SearchParams } from 'elasticsearch'; +import { SearchResponse } from 'elasticsearch'; import { sum as arraySum, min as arrayMin, max as arrayMax, get } from 'lodash'; import { MakeSchemaFrom } from 'src/plugins/usage_collection/server'; import { CANVAS_TYPE } from '../../common/lib/constants'; @@ -229,9 +229,10 @@ export function summarizeWorkpads(workpadDocs: CanvasWorkpad[]): WorkpadTelemetr variables: variableInfo, }; } +type ESResponse = SearchResponse; -const workpadCollector: TelemetryCollector = async function (kibanaIndex, callCluster) { - const searchParams: SearchParams = { +const workpadCollector: TelemetryCollector = async function (kibanaIndex, esClient) { + const searchParams = { size: 10000, // elasticsearch index.max_result_window default value index: kibanaIndex, ignoreUnavailable: true, @@ -239,7 +240,7 @@ const workpadCollector: TelemetryCollector = async function (kibanaIndex, callCl body: { query: { bool: { filter: { term: { type: CANVAS_TYPE } } } } }, }; - const esResponse = await callCluster('search', searchParams); + const { body: esResponse } = await esClient.search(searchParams); if (get(esResponse, 'hits.hits.length') > 0) { const workpads = esResponse.hits.hits.map((hit) => hit._source[CANVAS_TYPE]); diff --git a/x-pack/plugins/canvas/types/telemetry.ts b/x-pack/plugins/canvas/types/telemetry.ts index 3b635b2e57926..e323372e87993 100644 --- a/x-pack/plugins/canvas/types/telemetry.ts +++ b/x-pack/plugins/canvas/types/telemetry.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LegacyAPICaller } from 'kibana/server'; +import { ElasticsearchClient } from 'kibana/server'; /** Function for collecting information about canvas usage @@ -13,7 +13,7 @@ export type TelemetryCollector = ( /** The server instance */ kibanaIndex: string, /** Function for calling elasticsearch */ - callCluster: LegacyAPICaller + esClient: ElasticsearchClient ) => Record; export interface TelemetryCustomElementDocument { diff --git a/x-pack/plugins/data_enhanced/common/index.ts b/x-pack/plugins/data_enhanced/common/index.ts index 7b14a723d7877..f26ba0dd0b46c 100644 --- a/x-pack/plugins/data_enhanced/common/index.ts +++ b/x-pack/plugins/data_enhanced/common/index.ts @@ -15,4 +15,5 @@ export { BackgroundSessionSavedObjectAttributes, BackgroundSessionFindOptions, BackgroundSessionStatus, + BackgroundSessionSearchInfo, } from './search'; diff --git a/x-pack/plugins/data_enhanced/common/search/session/types.ts b/x-pack/plugins/data_enhanced/common/search/session/types.ts index 0b82c9160ea1a..1310c05ed6854 100644 --- a/x-pack/plugins/data_enhanced/common/search/session/types.ts +++ b/x-pack/plugins/data_enhanced/common/search/session/types.ts @@ -19,7 +19,12 @@ export interface BackgroundSessionSavedObjectAttributes { urlGeneratorId: string; initialState: Record; restoreState: Record; - idMapping: Record; + idMapping: Record; +} + +export interface BackgroundSessionSearchInfo { + id: string; // ID of the async search request + strategy: string; // Search strategy used to submit the search request } export interface BackgroundSessionFindOptions { diff --git a/x-pack/plugins/data_enhanced/public/search/ui/connected_background_session_indicator/connected_background_session_indicator.test.tsx b/x-pack/plugins/data_enhanced/public/search/ui/connected_background_session_indicator/connected_background_session_indicator.test.tsx index e08773c6a8a76..6fa9abd0f1ab6 100644 --- a/x-pack/plugins/data_enhanced/public/search/ui/connected_background_session_indicator/connected_background_session_indicator.test.tsx +++ b/x-pack/plugins/data_enhanced/public/search/ui/connected_background_session_indicator/connected_background_session_indicator.test.tsx @@ -57,8 +57,35 @@ test('should show indicator in case there is an active search session', async () await waitFor(() => getByTestId('backgroundSessionIndicator')); }); +test('should be disabled when permissions are off', async () => { + const state$ = new BehaviorSubject(SessionState.Loading); + coreStart.application.currentAppId$ = new BehaviorSubject('discover'); + (coreStart.application.capabilities as any) = { + discover: { + storeSearchSession: false, + }, + }; + const BackgroundSessionIndicator = createConnectedBackgroundSessionIndicator({ + sessionService: { ...sessionService, state$ }, + application: coreStart.application, + timeFilter, + }); + + render(); + + await waitFor(() => screen.getByTestId('backgroundSessionIndicator')); + + expect(screen.getByTestId('backgroundSessionIndicator').querySelector('button')).toBeDisabled(); +}); + test('should be disabled during auto-refresh', async () => { const state$ = new BehaviorSubject(SessionState.Loading); + coreStart.application.currentAppId$ = new BehaviorSubject('discover'); + (coreStart.application.capabilities as any) = { + discover: { + storeSearchSession: true, + }, + }; const BackgroundSessionIndicator = createConnectedBackgroundSessionIndicator({ sessionService: { ...sessionService, state$ }, application: coreStart.application, diff --git a/x-pack/plugins/data_enhanced/public/search/ui/connected_background_session_indicator/connected_background_session_indicator.tsx b/x-pack/plugins/data_enhanced/public/search/ui/connected_background_session_indicator/connected_background_session_indicator.tsx index b80295d87d202..1469c96d7166e 100644 --- a/x-pack/plugins/data_enhanced/public/search/ui/connected_background_session_indicator/connected_background_session_indicator.tsx +++ b/x-pack/plugins/data_enhanced/public/search/ui/connected_background_session_indicator/connected_background_session_indicator.tsx @@ -29,12 +29,35 @@ export const createConnectedBackgroundSessionIndicator = ({ .getRefreshIntervalUpdate$() .pipe(map(isAutoRefreshEnabled), distinctUntilChanged()); + const getCapabilitiesByAppId = ( + capabilities: ApplicationStart['capabilities'], + appId?: string + ) => { + switch (appId) { + case 'dashboards': + return capabilities.dashboard; + case 'discover': + return capabilities.discover; + default: + return undefined; + } + }; + return () => { const state = useObservable(sessionService.state$.pipe(debounceTime(500))); const autoRefreshEnabled = useObservable(isAutoRefreshEnabled$, isAutoRefreshEnabled()); + const appId = useObservable(application.currentAppId$, undefined); + let disabled = false; let disabledReasonText: string = ''; + if (getCapabilitiesByAppId(application.capabilities, appId)?.storeSearchSession !== true) { + disabled = true; + disabledReasonText = i18n.translate('xpack.data.backgroundSessionIndicator.noCapability', { + defaultMessage: "You don't have permissions to send to background.", + }); + } + if (autoRefreshEnabled) { disabled = true; disabledReasonText = i18n.translate( diff --git a/x-pack/plugins/data_enhanced/server/search/session/session_service.test.ts b/x-pack/plugins/data_enhanced/server/search/session/session_service.test.ts index 766de908353f5..f14df97f00c12 100644 --- a/x-pack/plugins/data_enhanced/server/search/session/session_service.test.ts +++ b/x-pack/plugins/data_enhanced/server/search/session/session_service.test.ts @@ -30,6 +30,7 @@ describe('BackgroundSessionService', () => { const MOCK_SESSION_ID = 'session-id-mock'; const MOCK_ASYNC_ID = '123456'; + const MOCK_STRATEGY = 'ese'; const MOCK_KEY_HASH = '608de49a4600dbb5b173492759792e4a'; const createMockInternalSavedObjectClient = ( @@ -47,7 +48,10 @@ describe('BackgroundSessionService', () => { attributes: { sessionId: MOCK_SESSION_ID, idMapping: { - 'another-key': 'another-async-id', + 'another-key': { + id: 'another-async-id', + strategy: 'another-strategy', + }, }, }, id: MOCK_SESSION_ID, @@ -283,7 +287,7 @@ describe('BackgroundSessionService', () => { await service.trackId( searchRequest, searchId, - { sessionId, isStored }, + { sessionId, isStored, strategy: MOCK_STRATEGY }, { savedObjectsClient } ); @@ -313,7 +317,8 @@ describe('BackgroundSessionService', () => { ); const [setSessionId, setParams] = setSpy.mock.calls[0]; - expect(setParams.ids.get(requestHash)).toBe(searchId); + expect(setParams.ids.get(requestHash).id).toBe(searchId); + expect(setParams.ids.get(requestHash).strategy).toBe(MOCK_STRATEGY); expect(setSessionId).toBe(sessionId); }); @@ -326,12 +331,17 @@ describe('BackgroundSessionService', () => { await service.trackId( searchRequest, searchId, - { sessionId, isStored }, + { sessionId, isStored, strategy: MOCK_STRATEGY }, { savedObjectsClient } ); expect(savedObjectsClient.update).toHaveBeenCalledWith(BACKGROUND_SESSION_TYPE, sessionId, { - idMapping: { [requestHash]: searchId }, + idMapping: { + [requestHash]: { + id: searchId, + strategy: MOCK_STRATEGY, + }, + }, }); }); }); @@ -380,7 +390,12 @@ describe('BackgroundSessionService', () => { name: 'my_name', appId: 'my_app_id', urlGeneratorId: 'my_url_generator_id', - idMapping: { [requestHash]: searchId }, + idMapping: { + [requestHash]: { + id: searchId, + strategy: MOCK_STRATEGY, + }, + }, }, references: [], }; @@ -419,7 +434,10 @@ describe('BackgroundSessionService', () => { const findSpy = jest.fn().mockResolvedValue({ saved_objects: [] }); createMockInternalSavedObjectClient(findSpy); - const mockIdMapping = createMockIdMapping([[MOCK_KEY_HASH, MOCK_ASYNC_ID]], moment()); + const mockIdMapping = createMockIdMapping( + [[MOCK_KEY_HASH, { id: MOCK_ASYNC_ID, strategy: MOCK_STRATEGY }]], + moment() + ); Object.defineProperty(service, 'sessionSearchMap', { get: () => mockIdMapping, @@ -438,7 +456,7 @@ describe('BackgroundSessionService', () => { createMockInternalSavedObjectClient(findSpy); const mockIdMapping = createMockIdMapping( - [[MOCK_KEY_HASH, MOCK_ASYNC_ID]], + [[MOCK_KEY_HASH, { id: MOCK_ASYNC_ID, strategy: MOCK_STRATEGY }]], moment().subtract(2, 'm') ); @@ -459,7 +477,7 @@ describe('BackgroundSessionService', () => { createMockInternalSavedObjectClient(findSpy); const mockIdMapping = createMockIdMapping( - [[MOCK_KEY_HASH, MOCK_ASYNC_ID]], + [[MOCK_KEY_HASH, { id: MOCK_ASYNC_ID, strategy: MOCK_STRATEGY }]], moment(), MAX_UPDATE_RETRIES ); @@ -528,7 +546,10 @@ describe('BackgroundSessionService', () => { attributes: { idMapping: { b: 'c', - [MOCK_KEY_HASH]: MOCK_ASYNC_ID, + [MOCK_KEY_HASH]: { + id: MOCK_ASYNC_ID, + strategy: MOCK_STRATEGY, + }, }, }, }, @@ -566,7 +587,10 @@ describe('BackgroundSessionService', () => { id: MOCK_SESSION_ID, attributes: { idMapping: { - b: 'c', + b: { + id: 'c', + strategy: MOCK_STRATEGY, + }, }, }, }, diff --git a/x-pack/plugins/data_enhanced/server/search/session/session_service.ts b/x-pack/plugins/data_enhanced/server/search/session/session_service.ts index d426e73b48510..01291919001f5 100644 --- a/x-pack/plugins/data_enhanced/server/search/session/session_service.ts +++ b/x-pack/plugins/data_enhanced/server/search/session/session_service.ts @@ -32,6 +32,7 @@ import { import { BackgroundSessionSavedObjectAttributes, BackgroundSessionFindOptions, + BackgroundSessionSearchInfo, BackgroundSessionStatus, } from '../../../common'; import { BACKGROUND_SESSION_TYPE } from '../../saved_objects'; @@ -51,7 +52,7 @@ export interface BackgroundSessionDependencies { export interface SessionInfo { insertTime: Moment; retryCount: number; - ids: Map; + ids: Map; } export class BackgroundSessionService implements ISessionService { @@ -316,25 +317,31 @@ export class BackgroundSessionService implements ISessionService { public trackId = async ( searchRequest: IKibanaSearchRequest, searchId: string, - { sessionId, isStored }: ISearchOptions, + { sessionId, isStored, strategy }: ISearchOptions, deps: BackgroundSessionDependencies ) => { if (!sessionId || !searchId) return; this.logger.debug(`trackId | ${sessionId} | ${searchId}`); const requestHash = createRequestHash(searchRequest.params); + const searchInfo = { + id: searchId, + strategy: strategy!, + }; // If there is already a saved object for this session, update it to include this request/ID. // Otherwise, just update the in-memory mapping for this session for when the session is saved. if (isStored) { - const attributes = { idMapping: { [requestHash]: searchId } }; + const attributes = { + idMapping: { [requestHash]: searchInfo }, + }; await this.update(sessionId, attributes, deps); } else { const map = this.sessionSearchMap.get(sessionId) ?? { insertTime: moment(), retryCount: 0, - ids: new Map(), + ids: new Map(), }; - map.ids.set(requestHash, searchId); + map.ids.set(requestHash, searchInfo); this.sessionSearchMap.set(sessionId, map); } }; @@ -363,7 +370,7 @@ export class BackgroundSessionService implements ISessionService { throw new Error('No search ID in this session matching the given search request'); } - return session.attributes.idMapping[requestHash]; + return session.attributes.idMapping[requestHash].id; }; public asScopedProvider = ({ savedObjects }: CoreStart) => { diff --git a/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.test.ts b/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.test.ts index e3730084d7020..1c6d7e4066187 100644 --- a/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.test.ts +++ b/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.test.ts @@ -443,3 +443,77 @@ describe('UrlDrilldown', () => { }); }); }); + +describe('encoding', () => { + const urlDrilldown = createDrilldown(); + const context: ActionContext = { + data: { + data: mockDataPoints, + }, + embeddable: mockEmbeddable, + }; + + test('encodes URL by default', async () => { + const config: Config = { + url: { + template: 'https://elastic.co?foo=head%26shoulders', + }, + openInNewTab: false, + }; + const url = await urlDrilldown.getHref(config, context); + + expect(url).toBe('https://elastic.co?foo=head%2526shoulders'); + }); + + test('encodes URL when encoding is enabled', async () => { + const config: Config = { + url: { + template: 'https://elastic.co?foo=head%26shoulders', + }, + openInNewTab: false, + encodeUrl: true, + }; + const url = await urlDrilldown.getHref(config, context); + + expect(url).toBe('https://elastic.co?foo=head%2526shoulders'); + }); + + test('does not encode URL when encoding is not enabled', async () => { + const config: Config = { + url: { + template: 'https://elastic.co?foo=head%26shoulders', + }, + openInNewTab: false, + encodeUrl: false, + }; + const url = await urlDrilldown.getHref(config, context); + + expect(url).toBe('https://elastic.co?foo=head%26shoulders'); + }); + + test('can encode URI component using "encodeURIComponent" Handlebars helper', async () => { + const config: Config = { + url: { + template: 'https://elastic.co?foo={{encodeURIComponent "head%26shoulders@gmail.com"}}', + }, + openInNewTab: false, + encodeUrl: false, + }; + const url = await urlDrilldown.getHref(config, context); + + expect(url).toBe('https://elastic.co?foo=head%2526shoulders%40gmail.com'); + }); + + test('can encode URI component using "encodeURIQuery" Handlebars helper', async () => { + const config: Config = { + url: { + template: 'https://elastic.co?foo={{encodeURIQuery "head%26shoulders@gmail.com"}}', + }, + openInNewTab: false, + encodeUrl: false, + }; + const url = await urlDrilldown.getHref(config, context); + + expect(url).toBe('https://elastic.co?foo=head%2526shoulders@gmail.com'); + }); +}); diff --git a/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.tsx b/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.tsx index bfeab263d20e3..ffb0687305168 100644 --- a/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.tsx +++ b/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.tsx @@ -104,7 +104,8 @@ export class UrlDrilldown implements Drilldown ({ url: { template: '' }, - openInNewTab: false, + openInNewTab: true, + encodeUrl: true, }); public readonly isConfigValid = (config: Config): config is Config => { @@ -133,7 +134,12 @@ export class UrlDrilldown implements Drilldown { copy: expect.any(Function), toggleIsHidden: expect.any(Function), isHidden: expect.any(Boolean), - text: •••••••, + text: ( + + ••••••• + + ), }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/credentials_list.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/credentials_list.tsx index 9240bade4975e..df85a9c3053a6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/credentials_list.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/credentials_list.tsx @@ -40,6 +40,7 @@ export const CredentialsList: React.FC = () => { { name: 'Key', width: '36%', + className: 'eui-textBreakAll', render: (token: ApiToken) => { const { key } = token; if (!key) return null; @@ -60,6 +61,10 @@ export const CredentialsList: React.FC = () => { ); }, + mobileOptions: { + // @ts-ignore - EUI's type definitions need to be updated + width: '100%', + }, }, { name: 'Modes', diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/key.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/key.tsx index fa2d124cbccdf..8ea2b6c284fc6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/key.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/key.tsx @@ -39,6 +39,7 @@ export const Key: React.FC = ({ copy, toggleIsHidden, isHidden, text }) = iconType={hideIcon} aria-label={hideIconLabel} aria-pressed={!isHidden} + style={{ marginRight: '0.25em' }} /> {text} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/constants.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/constants.tsx index 27c3410767d8a..c685ed8863985 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/constants.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/constants.tsx @@ -16,6 +16,34 @@ export const FLYOUT_CONTINUE_BUTTON = i18n.translate( 'xpack.enterpriseSearch.appSearch.documentCreation.flyoutContinue', { defaultMessage: 'Continue' } ); +export const FLYOUT_CLOSE_BUTTON = i18n.translate( + 'xpack.enterpriseSearch.appSearch.documentCreation.modalClose', + { defaultMessage: 'Close' } +); + +export const DOCUMENT_CREATION_ERRORS = { + TITLE: i18n.translate('xpack.enterpriseSearch.appSearch.documentCreation.errorsTitle', { + defaultMessage: 'Something went wrong. Please address the errors and try again.', + }), + NO_FILE: i18n.translate('xpack.enterpriseSearch.appSearch.documentCreation.noFileFound', { + defaultMessage: 'No file found.', + }), + NO_VALID_FILE: i18n.translate('xpack.enterpriseSearch.appSearch.documentCreation.noValidFile', { + defaultMessage: 'Problem parsing file.', + }), + NOT_VALID: i18n.translate('xpack.enterpriseSearch.appSearch.documentCreation.notValidJson', { + defaultMessage: 'Document contents must be a valid JSON array or object.', + }), +}; +export const DOCUMENT_CREATION_WARNINGS = { + TITLE: i18n.translate('xpack.enterpriseSearch.appSearch.documentCreation.warningsTitle', { + defaultMessage: 'Warning!', + }), + LARGE_FILE: i18n.translate('xpack.enterpriseSearch.appSearch.documentCreation.largeFile', { + defaultMessage: + "You're uploading an extremely large file. This could potentially lock your browser, or take a very long time to process. If possible, try splitting your data up into multiple smaller files.", + }), +}; // This is indented the way it is to work with ApiCodeExample. // Use dedent() when calling this alone diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/api_code_example.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/api_code_example.tsx index 9ebe404659ca2..c33cda9f7e429 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/api_code_example.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/api_code_example.tsx @@ -23,6 +23,8 @@ import { EuiBadge, EuiCode, EuiCodeBlock, + EuiFlexGroup, + EuiFlexItem, } from '@elastic/eui'; import { getEnterpriseSearchUrl } from '../../../../shared/enterprise_search_url'; @@ -95,8 +97,14 @@ export const FlyoutBody: React.FC = () => { - POST - {documentsApiUrl} + + + POST + + + {documentsApiUrl} + + {dedent(` diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.test.tsx index 50e4d473e5f78..39c6abcaab7b3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.test.tsx @@ -11,11 +11,14 @@ import React from 'react'; import { shallow } from 'enzyme'; import { EuiTextArea, EuiButtonEmpty, EuiButton } from '@elastic/eui'; +import { Errors } from '../creation_response_components'; import { PasteJsonText, FlyoutHeader, FlyoutBody, FlyoutFooter } from './paste_json_text'; describe('PasteJsonText', () => { const values = { textInput: 'hello world', + isUploading: false, + errors: [], configuredLimits: { engine: { maxDocumentByteSize: 102400, @@ -24,6 +27,7 @@ describe('PasteJsonText', () => { }; const actions = { setTextInput: jest.fn(), + onSubmitJson: jest.fn(), closeDocumentCreation: jest.fn(), }; @@ -58,6 +62,16 @@ describe('PasteJsonText', () => { textarea.simulate('change', { target: { value: 'dolor sit amet' } }); expect(actions.setTextInput).toHaveBeenCalledWith('dolor sit amet'); }); + + it('shows an error banner and sets invalid form props if errors exist', () => { + const wrapper = shallow(); + expect(wrapper.find(EuiTextArea).prop('isInvalid')).toBe(false); + + setMockValues({ ...values, errors: ['some error'] }); + rerender(wrapper); + expect(wrapper.find(EuiTextArea).prop('isInvalid')).toBe(true); + expect(wrapper.prop('banner').type).toEqual(Errors); + }); }); describe('FlyoutFooter', () => { @@ -68,6 +82,13 @@ describe('PasteJsonText', () => { expect(actions.closeDocumentCreation).toHaveBeenCalled(); }); + it('submits json', () => { + const wrapper = shallow(); + + wrapper.find(EuiButton).simulate('click'); + expect(actions.onSubmitJson).toHaveBeenCalled(); + }); + it('disables/enables the Continue button based on whether text has been entered', () => { const wrapper = shallow(); expect(wrapper.find(EuiButton).prop('isDisabled')).toBe(false); @@ -76,5 +97,14 @@ describe('PasteJsonText', () => { rerender(wrapper); expect(wrapper.find(EuiButton).prop('isDisabled')).toBe(true); }); + + it('sets isLoading based on isUploading', () => { + const wrapper = shallow(); + expect(wrapper.find(EuiButton).prop('isLoading')).toBe(false); + + setMockValues({ ...values, isUploading: true }); + rerender(wrapper); + expect(wrapper.find(EuiButton).prop('isLoading')).toBe(true); + }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.tsx index ad83e0eb1a286..b1f83095f30af 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.tsx @@ -25,6 +25,7 @@ import { import { AppLogic } from '../../../app_logic'; import { FLYOUT_ARIA_LABEL_ID, FLYOUT_CANCEL_BUTTON, FLYOUT_CONTINUE_BUTTON } from '../constants'; +import { Errors } from '../creation_response_components'; import { DocumentCreationLogic } from '../'; import './paste_json_text.scss'; @@ -55,11 +56,11 @@ export const FlyoutBody: React.FC = () => { const { configuredLimits } = useValues(AppLogic); const maxDocumentByteSize = configuredLimits?.engine?.maxDocumentByteSize; - const { textInput } = useValues(DocumentCreationLogic); + const { textInput, errors } = useValues(DocumentCreationLogic); const { setTextInput } = useActions(DocumentCreationLogic); return ( - + }>

{i18n.translate( @@ -76,6 +77,7 @@ export const FlyoutBody: React.FC = () => { setTextInput(e.target.value)} + isInvalid={errors.length > 0} aria-label={i18n.translate( 'xpack.enterpriseSearch.appSearch.documentCreation.pasteJsonText.label', { defaultMessage: 'Paste JSON here' } @@ -89,8 +91,8 @@ export const FlyoutBody: React.FC = () => { }; export const FlyoutFooter: React.FC = () => { - const { textInput } = useValues(DocumentCreationLogic); - const { closeDocumentCreation } = useActions(DocumentCreationLogic); + const { textInput, isUploading } = useValues(DocumentCreationLogic); + const { onSubmitJson, closeDocumentCreation } = useActions(DocumentCreationLogic); return ( @@ -99,7 +101,7 @@ export const FlyoutFooter: React.FC = () => { {FLYOUT_CANCEL_BUTTON} - + {FLYOUT_CONTINUE_BUTTON} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.test.tsx index 72a245df817ba..a5cb1885d9a04 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.test.tsx @@ -3,11 +3,6 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ import { setMockValues, setMockActions } from '../../../../__mocks__/kea.mock'; import { rerender } from '../../../../__mocks__'; @@ -16,12 +11,15 @@ import React from 'react'; import { shallow } from 'enzyme'; import { EuiFilePicker, EuiButtonEmpty, EuiButton } from '@elastic/eui'; +import { Errors } from '../creation_response_components'; import { UploadJsonFile, FlyoutHeader, FlyoutBody, FlyoutFooter } from './upload_json_file'; describe('UploadJsonFile', () => { const mockFile = new File(['mock'], 'mock.json', { type: 'application/json' }); const values = { fileInput: null, + isUploading: false, + errors: [], configuredLimits: { engine: { maxDocumentByteSize: 102400, @@ -30,6 +28,7 @@ describe('UploadJsonFile', () => { }; const actions = { setFileInput: jest.fn(), + onSubmitFile: jest.fn(), closeDocumentCreation: jest.fn(), }; @@ -63,6 +62,25 @@ describe('UploadJsonFile', () => { wrapper.find(EuiFilePicker).simulate('change', []); expect(actions.setFileInput).toHaveBeenCalledWith(null); }); + + it('sets isLoading based on isUploading', () => { + const wrapper = shallow(); + expect(wrapper.find(EuiFilePicker).prop('isLoading')).toBe(false); + + setMockValues({ ...values, isUploading: true }); + rerender(wrapper); + expect(wrapper.find(EuiFilePicker).prop('isLoading')).toBe(true); + }); + + it('shows an error banner and sets invalid form props if errors exist', () => { + const wrapper = shallow(); + expect(wrapper.find(EuiFilePicker).prop('isInvalid')).toBe(false); + + setMockValues({ ...values, errors: ['some error'] }); + rerender(wrapper); + expect(wrapper.find(EuiFilePicker).prop('isInvalid')).toBe(true); + expect(wrapper.prop('banner').type).toEqual(Errors); + }); }); describe('FlyoutFooter', () => { @@ -73,6 +91,13 @@ describe('UploadJsonFile', () => { expect(actions.closeDocumentCreation).toHaveBeenCalled(); }); + it('submits the json file', () => { + const wrapper = shallow(); + + wrapper.find(EuiButton).simulate('click'); + expect(actions.onSubmitFile).toHaveBeenCalled(); + }); + it('disables/enables the Continue button based on whether files have been uploaded', () => { const wrapper = shallow(); expect(wrapper.find(EuiButton).prop('isDisabled')).toBe(true); @@ -81,5 +106,14 @@ describe('UploadJsonFile', () => { rerender(wrapper); expect(wrapper.find(EuiButton).prop('isDisabled')).toBe(true); }); + + it('sets isLoading based on isUploading', () => { + const wrapper = shallow(); + expect(wrapper.find(EuiButton).prop('isLoading')).toBe(false); + + setMockValues({ ...values, isUploading: true }); + rerender(wrapper); + expect(wrapper.find(EuiButton).prop('isLoading')).toBe(true); + }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.tsx index 6c5b1de79c320..86841223c7255 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.tsx @@ -3,11 +3,6 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ import React from 'react'; import { useValues, useActions } from 'kea'; @@ -30,6 +25,7 @@ import { import { AppLogic } from '../../../app_logic'; import { FLYOUT_ARIA_LABEL_ID, FLYOUT_CANCEL_BUTTON, FLYOUT_CONTINUE_BUTTON } from '../constants'; +import { Errors } from '../creation_response_components'; import { DocumentCreationLogic } from '../'; export const UploadJsonFile: React.FC = () => ( @@ -59,10 +55,11 @@ export const FlyoutBody: React.FC = () => { const { configuredLimits } = useValues(AppLogic); const maxDocumentByteSize = configuredLimits?.engine?.maxDocumentByteSize; + const { isUploading, errors } = useValues(DocumentCreationLogic); const { setFileInput } = useActions(DocumentCreationLogic); return ( - + }>

{i18n.translate( @@ -80,14 +77,16 @@ export const FlyoutBody: React.FC = () => { onChange={(files) => setFileInput(files?.length ? files[0] : null)} accept="application/json" fullWidth + isLoading={isUploading} + isInvalid={errors.length > 0} /> ); }; export const FlyoutFooter: React.FC = () => { - const { fileInput } = useValues(DocumentCreationLogic); - const { closeDocumentCreation } = useActions(DocumentCreationLogic); + const { fileInput, isUploading } = useValues(DocumentCreationLogic); + const { onSubmitFile, closeDocumentCreation } = useActions(DocumentCreationLogic); return ( @@ -96,7 +95,7 @@ export const FlyoutFooter: React.FC = () => { {FLYOUT_CANCEL_BUTTON} - + {FLYOUT_CONTINUE_BUTTON} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/errors.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/errors.test.tsx new file mode 100644 index 0000000000000..ec73184621b53 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/errors.test.tsx @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { setMockValues } from '../../../../__mocks__/kea.mock'; + +import React from 'react'; +import { shallow } from 'enzyme'; +import { EuiCallOut } from '@elastic/eui'; + +import { Errors } from './'; + +describe('Errors', () => { + it('does not render if no errors or warnings to render', () => { + setMockValues({ errors: [], warnings: [] }); + const wrapper = shallow(); + + expect(wrapper.find(EuiCallOut)).toHaveLength(0); + }); + + it('renders errors', () => { + setMockValues({ errors: ['error 1', 'error 2'], warnings: [] }); + const wrapper = shallow(); + + expect(wrapper.find(EuiCallOut)).toHaveLength(1); + expect(wrapper.find(EuiCallOut).prop('title')).toEqual( + 'Something went wrong. Please address the errors and try again.' + ); + expect(wrapper.find('p').first().text()).toEqual('error 1'); + expect(wrapper.find('p').last().text()).toEqual('error 2'); + }); + + it('renders warnings', () => { + setMockValues({ errors: [], warnings: ['document size warning'] }); + const wrapper = shallow(); + + expect(wrapper.find(EuiCallOut)).toHaveLength(1); + expect(wrapper.find(EuiCallOut).prop('title')).toEqual('Warning!'); + expect(wrapper.find('p').text()).toEqual('document size warning'); + }); + + it('renders both errors and warnings', () => { + setMockValues({ errors: ['some error'], warnings: ['some warning'] }); + const wrapper = shallow(); + + expect(wrapper.find(EuiCallOut)).toHaveLength(2); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/errors.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/errors.tsx new file mode 100644 index 0000000000000..cf0c4e1c46a13 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/errors.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { useValues } from 'kea'; + +import { EuiCallOut } from '@elastic/eui'; + +import { DOCUMENT_CREATION_ERRORS, DOCUMENT_CREATION_WARNINGS } from '../constants'; +import { DocumentCreationLogic } from '../'; + +export const Errors: React.FC = () => { + const { errors, warnings } = useValues(DocumentCreationLogic); + + return ( + <> + {errors.length > 0 && ( + + {errors.map((message, index) => ( +

{message}

+ ))} + + )} + {warnings.length > 0 && ( + + {warnings.map((message, index) => ( +

{message}

+ ))} +
+ )} + + ); +}; diff --git a/x-pack/plugins/logstash/public/services/upgrade/index.js b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/index.ts old mode 100755 new mode 100644 similarity index 77% rename from x-pack/plugins/logstash/public/services/upgrade/index.js rename to x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/index.ts index 1c835b11ae423..eb4aec46d1f08 --- a/x-pack/plugins/logstash/public/services/upgrade/index.js +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/index.ts @@ -4,4 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -export { UpgradeService } from './upgrade_service'; +export { Errors } from './errors'; +export { Summary } from './summary'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/summary.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/summary.test.tsx new file mode 100644 index 0000000000000..9882166f63ba0 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/summary.test.tsx @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { setMockValues, setMockActions } from '../../../../__mocks__/kea.mock'; + +import React from 'react'; +import { shallow } from 'enzyme'; +import { EuiFlyoutBody, EuiCallOut, EuiButton } from '@elastic/eui'; + +import { + InvalidDocumentsSummary, + ValidDocumentsSummary, + SchemaFieldsSummary, +} from './summary_sections'; +import { Summary, FlyoutHeader, FlyoutBody, FlyoutFooter } from './summary'; + +describe('Summary', () => { + const values = { + summary: { + invalidDocuments: { + total: 0, + }, + }, + }; + const actions = { + setCreationStep: jest.fn(), + closeDocumentCreation: jest.fn(), + }; + + beforeEach(() => { + jest.clearAllMocks(); + setMockValues(values); + setMockActions(actions); + }); + + it('renders', () => { + const wrapper = shallow(); + expect(wrapper.find(FlyoutHeader)).toHaveLength(1); + expect(wrapper.find(FlyoutBody)).toHaveLength(1); + expect(wrapper.find(FlyoutFooter)).toHaveLength(1); + }); + + describe('FlyoutHeader', () => { + it('renders', () => { + const wrapper = shallow(); + expect(wrapper.find('h2').text()).toEqual('Indexing summary'); + }); + }); + + describe('FlyoutBody', () => { + it('renders', () => { + const wrapper = shallow(); + expect(wrapper.find(InvalidDocumentsSummary)).toHaveLength(1); + expect(wrapper.find(ValidDocumentsSummary)).toHaveLength(1); + expect(wrapper.find(SchemaFieldsSummary)).toHaveLength(1); + }); + + it('shows an error callout as a flyout banner when the upload contained invalid document(s)', () => { + setMockValues({ summary: { invalidDocuments: { total: 1 } } }); + const wrapper = shallow(); + const banner = wrapper.find(EuiFlyoutBody).prop('banner') as any; + + expect(banner.type).toEqual(EuiCallOut); + expect(banner.props.color).toEqual('danger'); + expect(banner.props.iconType).toEqual('alert'); + expect(banner.props.title).toEqual( + 'Something went wrong. Please address the errors and try again.' + ); + }); + }); + + describe('FlyoutFooter', () => { + it('closes the flyout', () => { + const wrapper = shallow(); + + wrapper.find(EuiButton).simulate('click'); + expect(actions.closeDocumentCreation).toHaveBeenCalled(); + }); + + it('shows a "Fix errors" button when the upload contained invalid document(s)', () => { + setMockValues({ summary: { invalidDocuments: { total: 5 } } }); + const wrapper = shallow(); + + wrapper.find(EuiButton).last().simulate('click'); + expect(actions.setCreationStep).toHaveBeenCalledWith(1); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/summary.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/summary.tsx new file mode 100644 index 0000000000000..7c7b2c805a710 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/summary.tsx @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { useValues, useActions } from 'kea'; + +import { i18n } from '@kbn/i18n'; +import { + EuiFlyoutHeader, + EuiTitle, + EuiFlyoutBody, + EuiCallOut, + EuiFlyoutFooter, + EuiFlexGroup, + EuiFlexItem, + EuiButton, +} from '@elastic/eui'; + +import { FLYOUT_ARIA_LABEL_ID, FLYOUT_CLOSE_BUTTON, DOCUMENT_CREATION_ERRORS } from '../constants'; +import { DocumentCreationStep } from '../types'; +import { DocumentCreationLogic } from '../'; + +import { + InvalidDocumentsSummary, + ValidDocumentsSummary, + SchemaFieldsSummary, +} from './summary_sections'; + +export const Summary: React.FC = () => { + return ( + <> + + + + + ); +}; + +export const FlyoutHeader: React.FC = () => { + return ( + + +

+ {i18n.translate('xpack.enterpriseSearch.appSearch.documentCreation.showSummary.title', { + defaultMessage: 'Indexing summary', + })} +

+
+
+ ); +}; + +export const FlyoutBody: React.FC = () => { + const { summary } = useValues(DocumentCreationLogic); + const hasInvalidDocuments = summary.invalidDocuments.total > 0; + const invalidDocumentsBanner = ( + + ); + + return ( + + + + + + ); +}; + +export const FlyoutFooter: React.FC = () => { + const { setCreationStep, closeDocumentCreation } = useActions(DocumentCreationLogic); + const { summary } = useValues(DocumentCreationLogic); + const hasInvalidDocuments = summary.invalidDocuments.total > 0; + + return ( + + + + {FLYOUT_CLOSE_BUTTON} + + {hasInvalidDocuments && ( + + setCreationStep(DocumentCreationStep.AddDocuments)}> + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.documentCreation.showSummary.fixErrors', + { defaultMessage: 'Fix errors' } + )} + + + )} + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/summary_documents.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/summary_documents.test.tsx new file mode 100644 index 0000000000000..790b0b7197383 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/summary_documents.test.tsx @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { shallow } from 'enzyme'; +import { EuiCodeBlock, EuiCallOut } from '@elastic/eui'; + +import { ExampleDocumentJson, MoreDocumentsText } from './summary_documents'; + +describe('ExampleDocumentJson', () => { + const exampleDocument = { hello: 'world' }; + const expectedJson = `{ + "hello": "world" +}`; + + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find(EuiCodeBlock).prop('children')).toEqual(expectedJson); + expect(wrapper.find(EuiCallOut)).toHaveLength(0); + }); + + it('renders invalid documents with error callouts', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find('h3').text()).toEqual('This document was not indexed!'); + expect(wrapper.find(EuiCallOut)).toHaveLength(2); + expect(wrapper.find(EuiCallOut).first().prop('title')).toEqual('Bad JSON error'); + expect(wrapper.find(EuiCallOut).last().prop('title')).toEqual('Schema error'); + }); +}); + +describe('MoreDocumentsText', () => { + it('renders', () => { + const wrapper = shallow(); + expect(wrapper.find('p').text()).toEqual('and 100 other documents.'); + + wrapper.setProps({ documents: 1 }); + expect(wrapper.find('p').text()).toEqual('and 1 other document.'); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/summary_documents.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/summary_documents.tsx new file mode 100644 index 0000000000000..338020d26dec0 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/summary_documents.tsx @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { Fragment } from 'react'; + +import { i18n } from '@kbn/i18n'; +import { EuiCodeBlock, EuiCallOut, EuiTitle, EuiText, EuiSpacer } from '@elastic/eui'; + +interface ExampleDocumentJsonProps { + document: object; + errors?: string[]; +} +export const ExampleDocumentJson: React.FC = ({ document, errors }) => { + return ( + <> + {errors && ( + <> + +

+ {i18n.translate( + 'xpack.enterpriseSearch.appSearch.documentCreation.showSummary.documentNotIndexed', + { defaultMessage: 'This document was not indexed!' } + )} +

+
+ + {errors.map((errorMessage, index) => ( + + + + + ))} + + )} + + {JSON.stringify(document, null, 2)} + + + + ); +}; + +interface MoreDocumentsTextProps { + documents: number; +} +export const MoreDocumentsText: React.FC = ({ documents }) => { + return ( + +

+ {i18n.translate( + 'xpack.enterpriseSearch.appSearch.documentCreation.showSummary.otherDocuments', + { + defaultMessage: + 'and {documents, number} other {documents, plural, one {document} other {documents}}.', + values: { documents }, + } + )} +

+
+ ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/summary_section.scss b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/summary_section.scss new file mode 100644 index 0000000000000..029fcdd25554c --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/summary_section.scss @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +.documentCreationSummarySection { + padding: $euiSize $euiSizeM; + color: $euiTextSubduedColor; + border-top: $euiBorderThin; + border-bottom: $euiBorderThin; + + & + & { + border-top: 0; + } + + &__title { + display: flex; + align-items: center; + height: $euiSizeL; + + .euiIcon { + margin-right: $euiSizeS; + } + } +} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/summary_section.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/summary_section.test.tsx new file mode 100644 index 0000000000000..0af2327c6bbac --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/summary_section.test.tsx @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { ReactElement } from 'react'; +import { shallow } from 'enzyme'; +import { EuiAccordion, EuiIcon } from '@elastic/eui'; + +import { SummarySectionAccordion, SummarySectionEmpty } from './summary_section'; + +describe('SummarySectionAccordion', () => { + const props = { + id: 'some-id', + status: 'success' as 'success' | 'error' | 'info', + title: 'Some title', + }; + + it('renders', () => { + const wrapper = shallow( + Hello World + ); + + expect(wrapper.type()).toEqual(EuiAccordion); + expect(wrapper.hasClass('documentCreationSummarySection')).toBe(true); + expect(wrapper.find(EuiAccordion).prop('children')).toEqual('Hello World'); + }); + + it('renders a title', () => { + const wrapper = shallow(); + const buttonContent = shallow(wrapper.find(EuiAccordion).prop('buttonContent') as ReactElement); + + expect(buttonContent.find('.documentCreationSummarySection__title').text()).toEqual( + 'Hello World' + ); + }); + + it('renders icons based on the status prop', () => { + const wrapper = shallow(); + const getIcon = () => { + const buttonContent = shallow( + wrapper.find(EuiAccordion).prop('buttonContent') as ReactElement + ); + return buttonContent.find(EuiIcon); + }; + + wrapper.setProps({ status: 'error' }); + expect(getIcon().prop('type')).toEqual('crossInACircleFilled'); + expect(getIcon().prop('color')).toEqual('danger'); + + wrapper.setProps({ status: 'success' }); + expect(getIcon().prop('type')).toEqual('checkInCircleFilled'); + expect(getIcon().prop('color')).toEqual('success'); + + wrapper.setProps({ status: 'info' }); + expect(getIcon().prop('type')).toEqual('iInCircle'); + expect(getIcon().prop('color')).toEqual('default'); + }); +}); + +describe('SummarySectionEmpty', () => { + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.hasClass('documentCreationSummarySection')).toBe(true); + expect(wrapper.find('.documentCreationSummarySection__title').text()).toEqual( + 'No new documents' + ); + expect(wrapper.find(EuiIcon).prop('type')).toEqual('iInCircle'); + expect(wrapper.find(EuiIcon).prop('color')).toEqual('default'); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/summary_section.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/summary_section.tsx new file mode 100644 index 0000000000000..d50779e7ff003 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/summary_section.tsx @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +import { EuiAccordion, EuiIcon } from '@elastic/eui'; + +import './summary_section.scss'; + +const ICON_PROPS = { + error: { type: 'crossInACircleFilled', color: 'danger' }, + success: { type: 'checkInCircleFilled', color: 'success' }, + info: { type: 'iInCircle', color: 'default' }, +}; + +interface SummarySectionAccordionProps { + id: string; + status: 'success' | 'error' | 'info'; + title: string; +} +export const SummarySectionAccordion: React.FC = ({ + id, + status, + title, + children, +}) => { + return ( + + + {title} +
+ } + > + {children} + + ); +}; + +interface SummarySectionEmptyProps { + title: string; +} +export const SummarySectionEmpty: React.FC = ({ title }) => { + return ( +
+
+ + {title} +
+
+ ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/summary_sections.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/summary_sections.test.tsx new file mode 100644 index 0000000000000..86cea8ef23587 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/summary_sections.test.tsx @@ -0,0 +1,168 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { setMockValues } from '../../../../__mocks__/kea.mock'; + +import React from 'react'; +import { shallow } from 'enzyme'; + +import { EuiBadge } from '@elastic/eui'; +import { SummarySectionAccordion, SummarySectionEmpty } from './summary_section'; +import { ExampleDocumentJson, MoreDocumentsText } from './summary_documents'; + +import { + InvalidDocumentsSummary, + ValidDocumentsSummary, + SchemaFieldsSummary, +} from './summary_sections'; + +describe('InvalidDocumentsSummary', () => { + const mockDocument = { hello: 'world' }; + const mockExample = { document: mockDocument, errors: ['bad schema'] }; + + it('renders', () => { + setMockValues({ + summary: { + invalidDocuments: { + total: 1, + examples: [mockExample], + }, + }, + }); + const wrapper = shallow(); + + expect(wrapper.find(SummarySectionAccordion).prop('title')).toEqual( + '1 document with errors...' + ); + expect(wrapper.find(ExampleDocumentJson)).toHaveLength(1); + expect(wrapper.find(MoreDocumentsText)).toHaveLength(0); + }); + + it('renders with MoreDocumentsText if more than 5 documents exist', () => { + setMockValues({ + summary: { + invalidDocuments: { + total: 100, + examples: [mockExample, mockExample, mockExample, mockExample, mockExample], + }, + }, + }); + const wrapper = shallow(); + + expect(wrapper.find(SummarySectionAccordion).prop('title')).toEqual( + '100 documents with errors...' + ); + expect(wrapper.find(ExampleDocumentJson)).toHaveLength(5); + expect(wrapper.find(MoreDocumentsText)).toHaveLength(1); + expect(wrapper.find(MoreDocumentsText).prop('documents')).toEqual(95); + }); + + it('does not render if there are no invalid documents', () => { + setMockValues({ + summary: { + invalidDocuments: { + total: 0, + examples: [], + }, + }, + }); + const wrapper = shallow(); + + expect(wrapper.isEmptyRender()).toBe(true); + }); +}); + +describe('ValidDocumentsSummary', () => { + const mockDocument = { hello: 'world' }; + + it('renders', () => { + setMockValues({ + summary: { + validDocuments: { + total: 1, + examples: [mockDocument], + }, + }, + }); + const wrapper = shallow(); + + expect(wrapper.find(SummarySectionAccordion).prop('title')).toEqual('Added 1 document.'); + expect(wrapper.find(ExampleDocumentJson)).toHaveLength(1); + expect(wrapper.find(MoreDocumentsText)).toHaveLength(0); + }); + + it('renders with MoreDocumentsText if more than 5 documents exist', () => { + setMockValues({ + summary: { + validDocuments: { + total: 7, + examples: [mockDocument, mockDocument, mockDocument, mockDocument, mockDocument], + }, + }, + }); + const wrapper = shallow(); + + expect(wrapper.find(SummarySectionAccordion).prop('title')).toEqual('Added 7 documents.'); + expect(wrapper.find(ExampleDocumentJson)).toHaveLength(5); + expect(wrapper.find(MoreDocumentsText)).toHaveLength(1); + expect(wrapper.find(MoreDocumentsText).prop('documents')).toEqual(2); + }); + + it('renders SummarySectionEmpty if there are no valid documents', () => { + setMockValues({ + summary: { + validDocuments: { + total: 0, + examples: [], + }, + }, + }); + const wrapper = shallow(); + + expect(wrapper.find(SummarySectionEmpty).prop('title')).toEqual('No new documents.'); + }); +}); + +describe('SchemaFieldsSummary', () => { + it('renders', () => { + setMockValues({ + summary: { + newSchemaFields: ['test'], + }, + }); + const wrapper = shallow(); + + expect(wrapper.find(SummarySectionAccordion).prop('title')).toEqual( + "Added 1 field to the Engine's schema." + ); + expect(wrapper.find(EuiBadge)).toHaveLength(1); + }); + + it('renders multiple new schema fields', () => { + setMockValues({ + summary: { + newSchemaFields: ['foo', 'bar', 'baz', 'qux', 'quux', 'quuz'], + }, + }); + const wrapper = shallow(); + + expect(wrapper.find(SummarySectionAccordion).prop('title')).toEqual( + "Added 6 fields to the Engine's schema." + ); + expect(wrapper.find(EuiBadge)).toHaveLength(6); + }); + + it('renders SummarySectionEmpty if there are no new schema fields', () => { + setMockValues({ + summary: { + newSchemaFields: [], + }, + }); + const wrapper = shallow(); + + expect(wrapper.find(SummarySectionEmpty).prop('title')).toEqual('No new schema fields.'); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/summary_sections.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/summary_sections.tsx new file mode 100644 index 0000000000000..2a13622dfbc8e --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_response_components/summary_sections.tsx @@ -0,0 +1,117 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { useValues } from 'kea'; + +import { i18n } from '@kbn/i18n'; +import { EuiFlexGroup, EuiFlexItem, EuiBadge } from '@elastic/eui'; + +import { DocumentCreationLogic } from '../'; + +import { SummarySectionAccordion, SummarySectionEmpty } from './summary_section'; +import { ExampleDocumentJson, MoreDocumentsText } from './summary_documents'; + +export const InvalidDocumentsSummary: React.FC = () => { + const { + summary: { invalidDocuments }, + } = useValues(DocumentCreationLogic); + + const hasInvalidDocuments = invalidDocuments.total > 0; + const unshownInvalidDocuments = invalidDocuments.total - invalidDocuments.examples.length; + + return hasInvalidDocuments ? ( + + {invalidDocuments.examples.map(({ document, errors }, index) => ( + + ))} + {unshownInvalidDocuments > 0 && } + + ) : null; +}; + +export const ValidDocumentsSummary: React.FC = () => { + const { + summary: { validDocuments }, + } = useValues(DocumentCreationLogic); + + const hasValidDocuments = validDocuments.total > 0; + const unshownValidDocuments = validDocuments.total - validDocuments.examples.length; + + return hasValidDocuments ? ( + + {validDocuments.examples.map((document, index) => ( + + ))} + {unshownValidDocuments > 0 && } + + ) : ( + + ); +}; + +export const SchemaFieldsSummary: React.FC = () => { + const { + summary: { newSchemaFields }, + } = useValues(DocumentCreationLogic); + + return newSchemaFields.length ? ( + + + {newSchemaFields.map((schemaField: string) => ( + + {schemaField} + + ))} + + + ) : ( + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_flyout.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_flyout.test.tsx index f2799cde41e97..cc9a671e41e5d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_flyout.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_flyout.test.tsx @@ -16,6 +16,7 @@ import { PasteJsonText, UploadJsonFile, } from './creation_mode_components'; +import { Summary } from './creation_response_components'; import { DocumentCreationStep } from './types'; import { DocumentCreationFlyout, FlyoutContent } from './document_creation_flyout'; @@ -82,28 +83,11 @@ describe('DocumentCreationFlyout', () => { }); }); - describe('creation steps', () => { - it('renders an error page', () => { - setMockValues({ ...values, creationStep: DocumentCreationStep.ShowError }); - const wrapper = shallow(); - - expect(wrapper.text()).toBe('DocumentCreationError'); // TODO: actual component - }); - - it('renders an error summary', () => { - setMockValues({ ...values, creationStep: DocumentCreationStep.ShowErrorSummary }); - const wrapper = shallow(); - - expect(wrapper.text()).toBe('DocumentCreationSummary'); // TODO: actual component - }); - - it('renders a success summary', () => { - setMockValues({ ...values, creationStep: DocumentCreationStep.ShowSuccessSummary }); - const wrapper = shallow(); + it('renders a summary', () => { + setMockValues({ ...values, creationStep: DocumentCreationStep.ShowSummary }); + const wrapper = shallow(); - // TODO: Figure out if the error and success summary should remain the same vs different components - expect(wrapper.text()).toBe('DocumentCreationSummary'); // TODO: actual component - }); + expect(wrapper.find(Summary)).toHaveLength(1); }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_flyout.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_flyout.tsx index ca52d14befb38..2dd00f0ded17d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_flyout.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_flyout.tsx @@ -19,6 +19,7 @@ import { PasteJsonText, UploadJsonFile, } from './creation_mode_components'; +import { Summary } from './creation_response_components'; export const DocumentCreationFlyout: React.FC = () => { const { closeDocumentCreation } = useActions(DocumentCreationLogic); @@ -48,11 +49,7 @@ export const FlyoutContent: React.FC = () => { case 'file': return ; } - case DocumentCreationStep.ShowError: - return <>DocumentCreationError; - case DocumentCreationStep.ShowErrorSummary: - return <>DocumentCreationSummary; - case DocumentCreationStep.ShowSuccessSummary: - return <>DocumentCreationSummary; + case DocumentCreationStep.ShowSummary: + return ; } }; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_logic.test.ts index 1145d7853cb1a..bb0103b07b072 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_logic.test.ts @@ -7,6 +7,20 @@ import { resetContext } from 'kea'; import dedent from 'dedent'; +jest.mock('./utils', () => ({ + readUploadedFileAsText: jest.fn(), +})); +import { readUploadedFileAsText } from './utils'; + +jest.mock('../../../shared/http', () => ({ + HttpLogic: { values: { http: { post: jest.fn() } } }, +})); +import { HttpLogic } from '../../../shared/http'; + +jest.mock('../engine', () => ({ + EngineLogic: { values: { engineName: 'test-engine' } }, +})); + import { DOCUMENTS_API_JSON_EXAMPLE } from './constants'; import { DocumentCreationStep } from './types'; import { DocumentCreationLogic } from './'; @@ -18,11 +32,29 @@ describe('DocumentCreationLogic', () => { creationStep: DocumentCreationStep.AddDocuments, textInput: dedent(DOCUMENTS_API_JSON_EXAMPLE), fileInput: null, + isUploading: false, + warnings: [], + errors: [], + summary: {}, }; const mockFile = new File(['mockFile'], 'mockFile.json'); - const mount = () => { - resetContext({}); + const mount = (defaults?: object) => { + if (!defaults) { + resetContext({}); + } else { + resetContext({ + defaults: { + enterprise_search: { + app_search: { + document_creation_logic: { + ...defaults, + }, + }, + }, + }, + }); + } DocumentCreationLogic.mount(); }; @@ -120,17 +152,39 @@ describe('DocumentCreationLogic', () => { }); }); }); + + describe('errors & warnings', () => { + it('should be cleared', () => { + mount({ errors: ['error'], warnings: ['warnings'] }); + DocumentCreationLogic.actions.closeDocumentCreation(); + + expect(DocumentCreationLogic.values).toEqual({ + ...DEFAULT_VALUES, + errors: [], + warnings: [], + }); + }); + }); + + describe('textInput & fileInput', () => { + it('should be reset to default values', () => { + mount({ textInput: 'test', fileInput: mockFile }); + DocumentCreationLogic.actions.closeDocumentCreation(); + + expect(DocumentCreationLogic.values).toEqual(DEFAULT_VALUES); + }); + }); }); describe('setCreationStep', () => { describe('creationStep', () => { it('should be set to the provided value', () => { mount(); - DocumentCreationLogic.actions.setCreationStep(DocumentCreationStep.ShowSuccessSummary); + DocumentCreationLogic.actions.setCreationStep(DocumentCreationStep.ShowSummary); expect(DocumentCreationLogic.values).toEqual({ ...DEFAULT_VALUES, - creationStep: 3, + creationStep: 2, }); }); }); @@ -163,5 +217,393 @@ describe('DocumentCreationLogic', () => { }); }); }); + + describe('setWarnings', () => { + describe('warnings', () => { + it('should be set to the provided value', () => { + mount(); + DocumentCreationLogic.actions.setWarnings(['warning!']); + + expect(DocumentCreationLogic.values).toEqual({ + ...DEFAULT_VALUES, + warnings: ['warning!'], + }); + }); + }); + }); + + describe('setErrors', () => { + describe('errors', () => { + beforeAll(() => { + mount(); + }); + + it('should be set to the provided value', () => { + DocumentCreationLogic.actions.setErrors(['error 1', 'error 2']); + + expect(DocumentCreationLogic.values).toEqual({ + ...DEFAULT_VALUES, + errors: ['error 1', 'error 2'], + }); + }); + + it('should gracefully array wrap single errors', () => { + DocumentCreationLogic.actions.setErrors('error'); + + expect(DocumentCreationLogic.values).toEqual({ + ...DEFAULT_VALUES, + errors: ['error'], + }); + }); + }); + + describe('isUploading', () => { + it('resets isUploading to false', () => { + mount({ isUploading: true }); + DocumentCreationLogic.actions.setErrors(['error']); + + expect(DocumentCreationLogic.values).toEqual({ + ...DEFAULT_VALUES, + errors: ['error'], + isUploading: false, + }); + }); + }); + }); + + describe('setSummary', () => { + const mockSummary = { + errors: [], + validDocuments: { + total: 1, + examples: [{ foo: 'bar' }], + }, + invalidDocuments: { + total: 0, + examples: [], + }, + newSchemaFields: ['foo'], + }; + + describe('summary', () => { + it('should be set to the provided value', () => { + mount(); + DocumentCreationLogic.actions.setSummary(mockSummary); + + expect(DocumentCreationLogic.values).toEqual({ + ...DEFAULT_VALUES, + summary: mockSummary, + }); + }); + }); + + describe('isUploading', () => { + it('resets isUploading to false', () => { + mount({ isUploading: true }); + DocumentCreationLogic.actions.setSummary(mockSummary); + + expect(DocumentCreationLogic.values).toEqual({ + ...DEFAULT_VALUES, + summary: mockSummary, + isUploading: false, + }); + }); + }); + }); + + describe('onSubmitFile', () => { + describe('with a valid file', () => { + beforeAll(() => { + mount({ fileInput: mockFile }); + jest.spyOn(DocumentCreationLogic.actions, 'onSubmitJson').mockImplementation(); + }); + + it('should read the text in the file and submit it as JSON', async () => { + (readUploadedFileAsText as jest.Mock).mockReturnValue(Promise.resolve('some mock text')); + await DocumentCreationLogic.actions.onSubmitFile(); + + expect(DocumentCreationLogic.values.textInput).toEqual('some mock text'); + expect(DocumentCreationLogic.actions.onSubmitJson).toHaveBeenCalled(); + }); + + it('should set isUploading to true', () => { + DocumentCreationLogic.actions.onSubmitFile(); + + expect(DocumentCreationLogic.values.isUploading).toEqual(true); + }); + }); + + describe('with an invalid file', () => { + beforeAll(() => { + mount({ fileInput: mockFile }); + jest.spyOn(DocumentCreationLogic.actions, 'onSubmitJson'); + jest.spyOn(DocumentCreationLogic.actions, 'setErrors'); + }); + + it('should return an error', async () => { + (readUploadedFileAsText as jest.Mock).mockReturnValue(Promise.reject()); + await DocumentCreationLogic.actions.onSubmitFile(); + + expect(DocumentCreationLogic.actions.onSubmitJson).not.toHaveBeenCalled(); + expect(DocumentCreationLogic.actions.setErrors).toHaveBeenCalledWith([ + 'Problem parsing file.', + ]); + }); + }); + + describe('without a file', () => { + beforeAll(() => { + mount(); + jest.spyOn(DocumentCreationLogic.actions, 'onSubmitJson'); + jest.spyOn(DocumentCreationLogic.actions, 'setErrors'); + }); + + it('should return an error', () => { + DocumentCreationLogic.actions.onSubmitFile(); + + expect(DocumentCreationLogic.actions.onSubmitJson).not.toHaveBeenCalled(); + expect(DocumentCreationLogic.actions.setErrors).toHaveBeenCalledWith(['No file found.']); + }); + }); + }); + + describe('onSubmitJson', () => { + describe('with large JSON files', () => { + beforeAll(() => { + mount(); + jest.spyOn(DocumentCreationLogic.actions, 'uploadDocuments').mockImplementation(); + jest.spyOn(DocumentCreationLogic.actions, 'setWarnings'); + }); + + it('should set a warning', () => { + jest.spyOn(global.Buffer, 'byteLength').mockImplementation(() => 55000000); // 55MB + DocumentCreationLogic.actions.onSubmitJson(); + + expect(DocumentCreationLogic.actions.setWarnings).toHaveBeenCalledWith([ + expect.stringContaining("You're uploading an extremely large file"), + ]); + + jest.restoreAllMocks(); + }); + }); + + describe('with invalid JSON', () => { + beforeAll(() => { + mount(); + jest.spyOn(DocumentCreationLogic.actions, 'uploadDocuments').mockImplementation(); + jest.spyOn(DocumentCreationLogic.actions, 'setErrors'); + }); + + it('should return malformed JSON errors', () => { + DocumentCreationLogic.actions.setTextInput('invalid JSON'); + DocumentCreationLogic.actions.onSubmitJson(); + + expect(DocumentCreationLogic.actions.setErrors).toHaveBeenCalledWith([ + 'Unexpected token i in JSON at position 0', + ]); + expect(DocumentCreationLogic.actions.uploadDocuments).not.toHaveBeenCalled(); + }); + + it('should error on non-array/object JSON', () => { + DocumentCreationLogic.actions.setTextInput('null'); + DocumentCreationLogic.actions.onSubmitJson(); + + expect(DocumentCreationLogic.actions.setErrors).toHaveBeenCalledWith([ + 'Document contents must be a valid JSON array or object.', + ]); + expect(DocumentCreationLogic.actions.uploadDocuments).not.toHaveBeenCalled(); + }); + }); + + describe('with valid JSON', () => { + beforeAll(() => { + mount(); + jest.spyOn(DocumentCreationLogic.actions, 'uploadDocuments').mockImplementation(); + jest.spyOn(DocumentCreationLogic.actions, 'setErrors'); + }); + + it('should accept an array of JSON objs', () => { + const mockJson = [{ foo: 'bar' }, { bar: 'baz' }]; + DocumentCreationLogic.actions.setTextInput('[{"foo":"bar"},{"bar":"baz"}]'); + DocumentCreationLogic.actions.onSubmitJson(); + + expect(DocumentCreationLogic.actions.uploadDocuments).toHaveBeenCalledWith({ + documents: mockJson, + }); + expect(DocumentCreationLogic.actions.setErrors).not.toHaveBeenCalled(); + }); + + it('should accept a single JSON obj', () => { + const mockJson = { foo: 'bar' }; + DocumentCreationLogic.actions.setTextInput('{"foo":"bar"}'); + DocumentCreationLogic.actions.onSubmitJson(); + + expect(DocumentCreationLogic.actions.uploadDocuments).toHaveBeenCalledWith({ + documents: [mockJson], + }); + expect(DocumentCreationLogic.actions.setErrors).not.toHaveBeenCalled(); + }); + }); + }); + + describe('uploadDocuments', () => { + describe('valid uploads', () => { + const mockValidDocuments = [{ foo: 'bar', bar: 'baz', qux: 'quux' }]; + const mockValidResponse = { + errors: [], + validDocuments: { total: 3, examples: mockValidDocuments }, + invalidDocuments: { total: 0, examples: [] }, + newSchemaFields: ['foo', 'bar', 'qux'], + }; + + beforeAll(() => { + mount(); + jest.spyOn(DocumentCreationLogic.actions, 'setSummary'); + jest.spyOn(DocumentCreationLogic.actions, 'setCreationStep'); + }); + + it('should set and show summary from the returned response', async () => { + const { http } = HttpLogic.values; + const promise = (http.post as jest.Mock).mockReturnValueOnce( + Promise.resolve(mockValidResponse) + ); + + await DocumentCreationLogic.actions.uploadDocuments({ documents: mockValidDocuments }); + await promise; + + expect(DocumentCreationLogic.actions.setSummary).toHaveBeenCalledWith(mockValidResponse); + expect(DocumentCreationLogic.actions.setCreationStep).toHaveBeenCalledWith( + DocumentCreationStep.ShowSummary + ); + }); + }); + + describe('invalid uploads', () => { + beforeAll(() => { + mount(); + jest.spyOn(DocumentCreationLogic.actions, 'setErrors'); + }); + + it('handles API errors', async () => { + const { http } = HttpLogic.values; + const promise = (http.post as jest.Mock).mockReturnValueOnce( + Promise.reject({ + body: { + statusCode: 400, + error: 'Bad Request', + message: 'Invalid request payload JSON format', + }, + }) + ); + + await DocumentCreationLogic.actions.uploadDocuments({ documents: [{}] }); + await promise; + + expect(DocumentCreationLogic.actions.setErrors).toHaveBeenCalledWith( + '[400 Bad Request] Invalid request payload JSON format' + ); + }); + + it('handles client-side errors', async () => { + const { http } = HttpLogic.values; + const promise = (http.post as jest.Mock).mockReturnValueOnce(new Error()); + + await DocumentCreationLogic.actions.uploadDocuments({ documents: [{}] }); + await promise; + + expect(DocumentCreationLogic.actions.setErrors).toHaveBeenCalledWith( + "Cannot read property 'total' of undefined" + ); + }); + + // NOTE: I can't seem to reproduce this in a production setting. + it('handles errors returned from the API', async () => { + const { http } = HttpLogic.values; + const promise = (http.post as jest.Mock).mockReturnValueOnce( + Promise.resolve({ + errors: ['JSON cannot be empty'], + }) + ); + + await DocumentCreationLogic.actions.uploadDocuments({ documents: [{}] }); + await promise; + + expect(DocumentCreationLogic.actions.setErrors).toHaveBeenCalledWith([ + 'JSON cannot be empty', + ]); + }); + }); + + describe('chunks large uploads', () => { + // Using an array of #s for speed, it doesn't really matter what the contents of the documents are for this test + const largeDocumentsArray = ([...Array(200).keys()] as unknown) as object[]; + + const mockFirstResponse = { + validDocuments: { total: 99, examples: largeDocumentsArray.slice(0, 98) }, + invalidDocuments: { + total: 1, + examples: [{ document: largeDocumentsArray[99], error: ['some error'] }], + }, + newSchemaFields: ['foo', 'bar'], + }; + const mockSecondResponse = { + validDocuments: { total: 99, examples: largeDocumentsArray.slice(1, 99) }, + invalidDocuments: { + total: 1, + examples: [{ document: largeDocumentsArray[0], error: ['another error'] }], + }, + newSchemaFields: ['bar', 'baz'], + }; + + beforeAll(() => { + mount(); + jest.spyOn(DocumentCreationLogic.actions, 'setSummary'); + jest.spyOn(DocumentCreationLogic.actions, 'setErrors'); + }); + + it('should correctly merge multiple API calls into a single summary obj', async () => { + const { http } = HttpLogic.values; + const promise = (http.post as jest.Mock) + .mockReturnValueOnce(mockFirstResponse) + .mockReturnValueOnce(mockSecondResponse); + + await DocumentCreationLogic.actions.uploadDocuments({ documents: largeDocumentsArray }); + await promise; + + expect(http.post).toHaveBeenCalledTimes(2); + expect(DocumentCreationLogic.actions.setSummary).toHaveBeenCalledWith({ + errors: [], + validDocuments: { + total: 198, + examples: largeDocumentsArray.slice(0, 5), + }, + invalidDocuments: { + total: 2, + examples: [ + { document: largeDocumentsArray[99], error: ['some error'] }, + { document: largeDocumentsArray[0], error: ['another error'] }, + ], + }, + newSchemaFields: ['foo', 'bar', 'baz'], + }); + }); + + it('should correctly merge response errors', async () => { + const { http } = HttpLogic.values; + const promise = (http.post as jest.Mock) + .mockReturnValueOnce({ ...mockFirstResponse, errors: ['JSON cannot be empty'] }) + .mockReturnValueOnce({ ...mockSecondResponse, errors: ['Too large to render'] }); + + await DocumentCreationLogic.actions.uploadDocuments({ documents: largeDocumentsArray }); + await promise; + + expect(http.post).toHaveBeenCalledTimes(2); + expect(DocumentCreationLogic.actions.setErrors).toHaveBeenCalledWith([ + 'JSON cannot be empty', + 'Too large to render', + ]); + }); + }); + }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_logic.ts index 5b85e7f2ab54e..119baed74f684 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_logic.ts @@ -6,9 +6,18 @@ import { kea, MakeLogicType } from 'kea'; import dedent from 'dedent'; +import { isPlainObject, chunk, uniq } from 'lodash'; -import { DOCUMENTS_API_JSON_EXAMPLE } from './constants'; -import { DocumentCreationMode, DocumentCreationStep } from './types'; +import { HttpLogic } from '../../../shared/http'; +import { EngineLogic } from '../engine'; + +import { + DOCUMENTS_API_JSON_EXAMPLE, + DOCUMENT_CREATION_ERRORS, + DOCUMENT_CREATION_WARNINGS, +} from './constants'; +import { DocumentCreationMode, DocumentCreationStep, DocumentCreationSummary } from './types'; +import { readUploadedFileAsText } from './utils'; interface DocumentCreationValues { isDocumentCreationOpen: boolean; @@ -16,6 +25,10 @@ interface DocumentCreationValues { creationStep: DocumentCreationStep; textInput: string; fileInput: File | null; + isUploading: boolean; + warnings: string[]; + errors: string[]; + summary: DocumentCreationSummary; } interface DocumentCreationActions { @@ -25,6 +38,12 @@ interface DocumentCreationActions { setCreationStep(creationStep: DocumentCreationStep): { creationStep: DocumentCreationStep }; setTextInput(textInput: string): { textInput: string }; setFileInput(fileInput: File | null): { fileInput: File | null }; + setWarnings(warnings: string[]): { warnings: string[] }; + setErrors(errors: string[] | string): { errors: string[] }; + setSummary(summary: DocumentCreationSummary): { summary: DocumentCreationSummary }; + onSubmitFile(): void; + onSubmitJson(): void; + uploadDocuments(args: { documents: object[] }): { documents: object[] }; } export const DocumentCreationLogic = kea< @@ -38,6 +57,12 @@ export const DocumentCreationLogic = kea< setCreationStep: (creationStep) => ({ creationStep }), setTextInput: (textInput) => ({ textInput }), setFileInput: (fileInput) => ({ fileInput }), + setWarnings: (warnings) => ({ warnings }), + setErrors: (errors) => ({ errors }), + setSummary: (summary) => ({ summary }), + onSubmitJson: () => null, + onSubmitFile: () => null, + uploadDocuments: ({ documents }) => ({ documents }), }), reducers: () => ({ isDocumentCreationOpen: [ @@ -66,13 +91,134 @@ export const DocumentCreationLogic = kea< dedent(DOCUMENTS_API_JSON_EXAMPLE), { setTextInput: (_, { textInput }) => textInput, + closeDocumentCreation: () => dedent(DOCUMENTS_API_JSON_EXAMPLE), }, ], fileInput: [ null, { setFileInput: (_, { fileInput }) => fileInput, + closeDocumentCreation: () => null, + }, + ], + isUploading: [ + false, + { + onSubmitFile: () => true, + onSubmitJson: () => true, + setErrors: () => false, + setSummary: () => false, + }, + ], + warnings: [ + [], + { + onSubmitJson: () => [], + setWarnings: (_, { warnings }) => warnings, + closeDocumentCreation: () => [], + }, + ], + errors: [ + [], + { + onSubmitJson: () => [], + setErrors: (_, { errors }) => (Array.isArray(errors) ? errors : [errors]), + closeDocumentCreation: () => [], }, ], + summary: [ + {} as DocumentCreationSummary, + { + setSummary: (_, { summary }) => summary, + }, + ], + }), + listeners: ({ values, actions }) => ({ + onSubmitFile: async () => { + const { fileInput } = values; + + if (!fileInput) { + return actions.setErrors([DOCUMENT_CREATION_ERRORS.NO_FILE]); + } + try { + const textInput = await readUploadedFileAsText(fileInput); + actions.setTextInput(textInput); + actions.onSubmitJson(); + } catch { + actions.setErrors([DOCUMENT_CREATION_ERRORS.NO_VALID_FILE]); + } + }, + onSubmitJson: () => { + const { textInput } = values; + + const MAX_UPLOAD_BYTES = 50 * 1000000; // 50 MB + if (Buffer.byteLength(textInput) > MAX_UPLOAD_BYTES) { + actions.setWarnings([DOCUMENT_CREATION_WARNINGS.LARGE_FILE]); + } + + let documents; + try { + documents = JSON.parse(textInput); + } catch (error) { + return actions.setErrors([error.message]); + } + + if (Array.isArray(documents)) { + actions.uploadDocuments({ documents }); + } else if (isPlainObject(documents)) { + actions.uploadDocuments({ documents: [documents] }); + } else { + actions.setErrors([DOCUMENT_CREATION_ERRORS.NOT_VALID]); + } + }, + uploadDocuments: async ({ documents }) => { + const { http } = HttpLogic.values; + const { engineName } = EngineLogic.values; + + const CHUNK_SIZE = 100; + const MAX_EXAMPLES = 5; + + const promises = chunk(documents, CHUNK_SIZE).map((documentsChunk) => { + const body = JSON.stringify({ documents: documentsChunk }); + return http.post(`/api/app_search/engines/${engineName}/documents`, { body }); + }); + + try { + const responses = await Promise.all(promises); + const summary: DocumentCreationSummary = { + errors: [], + validDocuments: { total: 0, examples: [] }, + invalidDocuments: { total: 0, examples: [] }, + newSchemaFields: [], + }; + responses.forEach((response) => { + if (response.errors?.length > 0) { + summary.errors = uniq([...summary.errors, ...response.errors]); + return; + } + summary.validDocuments.total += response.validDocuments.total; + summary.invalidDocuments.total += response.invalidDocuments.total; + summary.validDocuments.examples = [ + ...summary.validDocuments.examples, + ...response.validDocuments.examples, + ].slice(0, MAX_EXAMPLES); + summary.invalidDocuments.examples = [ + ...summary.invalidDocuments.examples, + ...response.invalidDocuments.examples, + ].slice(0, MAX_EXAMPLES); + summary.newSchemaFields = uniq([...summary.newSchemaFields, ...response.newSchemaFields]); + }); + + if (summary.errors.length > 0) { + actions.setErrors(summary.errors); + } else { + actions.setSummary(summary); + actions.setCreationStep(DocumentCreationStep.ShowSummary); + } + } catch ({ body, message }) { + const errors = body ? `[${body.statusCode} ${body.error}] ${body.message}` : message; + actions.setErrors(errors); + } + }, }), }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/types.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/types.ts index d29bff162c197..ba641326f76b2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/types.ts @@ -9,7 +9,21 @@ export type DocumentCreationMode = 'text' | 'file' | 'api'; export enum DocumentCreationStep { ShowCreationModes, AddDocuments, - ShowErrorSummary, - ShowSuccessSummary, - ShowError, + ShowSummary, +} + +export interface DocumentCreationSummary { + errors: string[]; + validDocuments: { + total: number; + examples: object[]; + }; + invalidDocuments: { + total: number; + examples: Array<{ + document: object; + errors: string[]; + }>; + }; + newSchemaFields: string[]; } diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/utils.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/utils.test.ts new file mode 100644 index 0000000000000..0df98c8d3030e --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/utils.test.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { readUploadedFileAsText } from './utils'; + +describe('readUploadedFileAsText', () => { + it('reads a file as text', async () => { + const file = new File(['a mock file'], 'mockFile.json'); + const text = await readUploadedFileAsText(file); + expect(text).toEqual('a mock file'); + }); + + it('throws an error if the file cannot be read', async () => { + const badFile = ('causes an error' as unknown) as File; + await expect(readUploadedFileAsText(badFile)).rejects.toThrow(); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/utils.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/utils.ts new file mode 100644 index 0000000000000..d2b207c51d22a --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/utils.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const readUploadedFileAsText = (fileInput: File): Promise => { + const reader = new FileReader(); + + return new Promise((resolve, reject) => { + reader.onload = () => { + resolve(reader.result as string); + }; + try { + reader.readAsText(fileInput); + } catch { + reader.abort(); + reject(new Error()); + } + }); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/build_search_ui_config.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/build_search_ui_config.ts index 533adbaf5bab9..78e1fa9e7f3a2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/build_search_ui_config.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/build_search_ui_config.ts @@ -16,19 +16,16 @@ export const buildSearchUIConfig = (apiConnector: object, schema: Schema) => { sortField: 'id', }, searchQuery: { - result_fields: Object.keys(schema || {}).reduce( - (acc: { [key: string]: object }, key: string) => { - acc[key] = { - snippet: { - size: 300, - fallback: true, - }, - raw: {}, - }; - return acc; - }, - {} - ), + result_fields: Object.keys(schema).reduce((acc: { [key: string]: object }, key: string) => { + acc[key] = { + snippet: { + size: 300, + fallback: true, + }, + raw: {}, + }; + return acc; + }, {}), }, }; }; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.test.tsx index a46ec560a13e0..8fc1ed5a0a4b6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.test.tsx @@ -46,30 +46,32 @@ describe('SearchExperienceContent', () => { expect(wrapper.isEmptyRender()).toBe(false); }); - it('passes engineName and schema to the result view', () => { - const props = { - result: { - id: { - raw: '1', - }, - _meta: { - id: '1', - scopedId: '1', - score: 100, - engine: 'my-engine', - }, - foo: { - raw: 'bar', - }, + it('passes result, schema, and isMetaEngine to the result view', () => { + const result = { + id: { + raw: '1', }, - schemaForTypeHighlights: { - title: 'string' as SchemaTypes, + _meta: { + id: '1', + score: 100, + engine: 'my-engine', + }, + foo: { + raw: 'bar', }, }; const wrapper = shallow(); const resultView: any = wrapper.find(Results).prop('resultView'); - expect(resultView(props)).toEqual(); + expect(resultView({ result })).toEqual( + + ); }); it('renders pagination', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.tsx index 55a8377261dd9..b44f3115932a3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.tsx @@ -14,12 +14,12 @@ import { useValues } from 'kea'; import { ResultView } from './views'; import { Pagination } from './pagination'; -import { Props as ResultViewProps } from './views/result_view'; import { useSearchContextState } from './hooks'; import { DocumentCreationButton } from '../document_creation_button'; import { AppLogic } from '../../../app_logic'; import { EngineLogic } from '../../engine'; import { DOCS_PREFIX } from '../../../routes'; +import { Result } from '../../result/types'; export const SearchExperienceContent: React.FC = () => { const { resultSearchTerm, totalResults, wasSearched } = useSearchContextState(); @@ -43,8 +43,14 @@ export const SearchExperienceContent: React.FC = () => { { - return ; + resultView={({ result }: { result: Result }) => { + return ( + + ); }} /> diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.test.tsx index 91334f312623d..d3a61c12901d3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.test.tsx @@ -22,7 +22,6 @@ describe('ResultView', () => { }, _meta: { id: '1', - scopedId: '1', score: 100, engine: 'my-engine', }, @@ -33,11 +32,14 @@ describe('ResultView', () => { }; it('renders', () => { - const wrapper = shallow(); + const wrapper = shallow( + + ); expect(wrapper.find(Result).props()).toEqual({ result, shouldLinkToDetailPage: true, schemaForTypeHighlights: schema, + isMetaEngine: true, }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.tsx index 543c63b334940..2a17dd6128536 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.tsx @@ -13,15 +13,17 @@ import { Result } from '../../../result/result'; export interface Props { result: ResultType; schemaForTypeHighlights?: Schema; + isMetaEngine: boolean; } -export const ResultView: React.FC = ({ result, schemaForTypeHighlights }) => { +export const ResultView: React.FC = ({ result, schemaForTypeHighlights, isMetaEngine }) => { return (
  • ); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx index e8609c169855b..74976c1c61f0c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx @@ -23,17 +23,22 @@ import { EngineOverview } from '../engine_overview'; import { EngineRouter } from './'; describe('EngineRouter', () => { - const values = { dataLoading: false, engineNotFound: false, myRole: {} }; + const values = { + dataLoading: false, + engineNotFound: false, + myRole: {}, + engineName: 'some-engine', + }; const actions = { setEngineName: jest.fn(), initializeEngine: jest.fn(), clearEngine: jest.fn() }; beforeEach(() => { setMockValues(values); setMockActions(actions); + (useParams as jest.Mock).mockReturnValue({ engineName: 'some-engine' }); }); describe('useEffect', () => { beforeEach(() => { - (useParams as jest.Mock).mockReturnValue({ engineName: 'some-engine' }); shallow(); }); @@ -45,7 +50,7 @@ describe('EngineRouter', () => { expect(actions.initializeEngine).toHaveBeenCalled(); }); - it('clears engine on unmount', () => { + it('clears engine on unmount and on update', () => { unmountHandler(); expect(actions.clearEngine).toHaveBeenCalled(); }); @@ -68,6 +73,16 @@ describe('EngineRouter', () => { expect(wrapper.find(Loading)).toHaveLength(1); }); + // This would happen if a user jumps around from one engine route to another. If the engine name + // on the path has changed, but we still have an engine stored in state, we do not want to load + // any route views as they would be rendering with the wrong data. + it('renders a loading component if the engine stored in state is stale', () => { + setMockValues({ ...values, engineName: 'some-engine' }); + (useParams as jest.Mock).mockReturnValue({ engineName: 'some-new-engine' }); + const wrapper = shallow(); + expect(wrapper.find(Loading)).toHaveLength(1); + }); + it('renders a default engine overview', () => { const wrapper = shallow(); @@ -76,7 +91,7 @@ describe('EngineRouter', () => { }); it('renders an analytics view', () => { - setMockValues({ myRole: { canViewEngineAnalytics: true } }); + setMockValues({ ...values, myRole: { canViewEngineAnalytics: true } }); const wrapper = shallow(); expect(wrapper.find('[data-test-subj="AnalyticsTODO"]')).toHaveLength(1); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx index 9e0b043a87364..d3501a5ee7af3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx @@ -69,29 +69,31 @@ export const EngineRouter: React.FC = () => { }, } = useValues(AppLogic); - const { dataLoading, engineNotFound } = useValues(EngineLogic); + const { engineName, dataLoading, engineNotFound } = useValues(EngineLogic); const { setEngineName, initializeEngine, clearEngine } = useActions(EngineLogic); - const { engineName } = useParams() as { engineName: string }; - const engineBreadcrumb = [ENGINES_TITLE, engineName]; + const { engineName: engineNameParam } = useParams() as { engineName: string }; + const engineBreadcrumb = [ENGINES_TITLE, engineNameParam]; + + const isEngineInStateStale = () => engineName !== engineNameParam; useEffect(() => { - setEngineName(engineName); + setEngineName(engineNameParam); initializeEngine(); return clearEngine; - }, [engineName]); + }, [engineNameParam]); if (engineNotFound) { setQueuedErrorMessage( i18n.translate('xpack.enterpriseSearch.appSearch.engine.notFound', { - defaultMessage: "No engine with name '{engineName}' could be found.", - values: { engineName }, + defaultMessage: "No engine with name '{engineNameParam}' could be found.", + values: { engineNameParam }, }) ); return ; } - if (dataLoading) return ; + if (isEngineInStateStale() || dataLoading) return ; return ( diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/library/library.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/library/library.tsx index 1b222cfaacf7c..24d2fea973e14 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/library/library.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/library/library.tsx @@ -20,13 +20,13 @@ import { Result } from '../result/result'; export const Library: React.FC = () => { const props = { + isMetaEngine: false, result: { id: { raw: '1', }, _meta: { id: '1', - scopedId: '1', score: 100, engine: 'my-engine', }, @@ -98,6 +98,7 @@ export const Library: React.FC = () => { { { { { { }, _meta: { id: 'my-id-is-a-really-long-id-yes-it-is', - scopedId: '2', score: 100, engine: 'my-engine-is-a-really-long-engin-name-yes-it-is', }, diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.scss b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.scss index 8342061ee00c3..f69acbdaba150 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.scss +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.scss @@ -1,17 +1,43 @@ .appSearchResult { - display: flex; + display: grid; + grid-template-columns: 1fr auto; + grid-template-rows: 1fr auto; + grid-template-areas: + 'content actions' + 'toggle actions'; + overflow: hidden; // Prevents child background-colors from clipping outside of panel border-radius &__content { + grid-area: content; width: 100%; padding: $euiSize; overflow: hidden; color: $euiTextColor; } - &__hiddenFieldsIndicator { + &__hiddenFieldsToggle { + grid-area: toggle; + display: flex; + justify-content: center; + padding: $euiSizeS; + border-top: $euiBorderThin; font-size: $euiFontSizeXS; - color: $euiColorDarkShade; - margin-top: $euiSizeS; + color: $euiColorPrimary; + + &:hover, + &:focus { + background-color: $euiPageBackgroundColor; + } + + .euiIcon { + margin-left: $euiSizeXS; + } + } + + &__actionButtons { + grid-area: actions; + display: flex; + flex-wrap: no-wrap; } &__actionButton { @@ -22,10 +48,27 @@ border-left: $euiBorderThin; &:hover, - &:focus, - &:active { + &:focus { background-color: $euiPageBackgroundColor; - cursor: pointer; } } } + +/** + * CSS for hover specific logic + * It's mildly horrific, so I pulled it out to its own section here + */ + +.appSearchResult--link { + &:hover, + &:focus { + @include euiSlightShadowHover; + } +} +.appSearchResult__content--link:hover { + cursor: pointer; + + & ~ .appSearchResult__actionButtons .appSearchResult__actionButton--link { + background-color: $euiPageBackgroundColor; + } +} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.test.tsx index 5b598a0b8565e..973fc6226910a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.test.tsx @@ -18,6 +18,7 @@ import { Result } from './result'; describe('Result', () => { const props = { + isMetaEngine: false, result: { id: { raw: '1', @@ -33,7 +34,6 @@ describe('Result', () => { }, _meta: { id: '1', - scopedId: '1', score: 100, engine: 'my-engine', }, @@ -49,6 +49,7 @@ describe('Result', () => { it('renders', () => { const wrapper = shallow(); expect(wrapper.find(EuiPanel).exists()).toBe(true); + expect(wrapper.find(EuiPanel).prop('title')).toEqual('Document 1'); }); it('should render a ResultField for each field except id and _meta', () => { @@ -60,30 +61,36 @@ describe('Result', () => { ]); }); - it('passes through showScore and resultMeta to ResultHeader', () => { - const wrapper = shallow(); - expect(wrapper.find(ResultHeader).prop('showScore')).toBe(true); - expect(wrapper.find(ResultHeader).prop('resultMeta')).toEqual({ - id: '1', - scopedId: '1', - score: 100, - engine: 'my-engine', + it('passes showScore, resultMeta, and isMetaEngine to ResultHeader', () => { + const wrapper = shallow(); + expect(wrapper.find(ResultHeader).props()).toEqual({ + isMetaEngine: true, + showScore: true, + resultMeta: { + id: '1', + score: 100, + engine: 'my-engine', + }, }); }); describe('document detail link', () => { it('will render a link if shouldLinkToDetailPage is true', () => { const wrapper = shallow(); - expect(wrapper.find(ReactRouterHelper).prop('to')).toEqual('/engines/my-engine/documents/1'); - expect(wrapper.find('article.appSearchResult__content').exists()).toBe(false); - expect(wrapper.find('a.appSearchResult__content').exists()).toBe(true); + wrapper.find(ReactRouterHelper).forEach((link) => { + expect(link.prop('to')).toEqual('/engines/my-engine/documents/1'); + }); + expect(wrapper.hasClass('appSearchResult--link')).toBe(true); + expect(wrapper.find('.appSearchResult__content--link').exists()).toBe(true); + expect(wrapper.find('.appSearchResult__actionButton--link').exists()).toBe(true); }); it('will not render a link if shouldLinkToDetailPage is not set', () => { const wrapper = shallow(); expect(wrapper.find(ReactRouterHelper).exists()).toBe(false); - expect(wrapper.find('article.appSearchResult__content').exists()).toBe(true); - expect(wrapper.find('a.appSearchResult__content').exists()).toBe(false); + expect(wrapper.hasClass('appSearchResult--link')).toBe(false); + expect(wrapper.find('.appSearchResult__content--link').exists()).toBe(false); + expect(wrapper.find('.appSearchResult__actionButton--link').exists()).toBe(false); }); }); @@ -100,6 +107,7 @@ describe('Result', () => { describe('when there are more than 5 fields', () => { const propsWithMoreFields = { + isMetaEngine: false, result: { id: { raw: '1', @@ -124,7 +132,6 @@ describe('Result', () => { }, _meta: { id: '1', - scopedId: '1', score: 100, engine: 'my-engine', }, @@ -138,18 +145,16 @@ describe('Result', () => { wrapper = shallow(); }); - it('renders a collapse button', () => { - expect(wrapper.find('[data-test-subj="CollapseResult"]').exists()).toBe(false); + it('renders a hidden fields toggle button', () => { + expect(wrapper.find('.appSearchResult__hiddenFieldsToggle').exists()).toBe(true); }); - it('does not render an expand button', () => { - expect(wrapper.find('[data-test-subj="ExpandResult"]').exists()).toBe(true); + it('renders a collapse icon', () => { + expect(wrapper.find('[data-test-subj="CollapseResult"]').exists()).toBe(false); }); - it('renders a hidden fields indicator', () => { - expect(wrapper.find('.appSearchResult__hiddenFieldsIndicator').text()).toEqual( - '1 more fields' - ); + it('does not render an expand icon', () => { + expect(wrapper.find('[data-test-subj="ExpandResult"]').exists()).toBe(true); }); it('shows no more than 5 fields', () => { @@ -162,20 +167,22 @@ describe('Result', () => { beforeAll(() => { wrapper = shallow(); - expect(wrapper.find('.appSearchResult__actionButton').exists()).toBe(true); - wrapper.find('.appSearchResult__actionButton').simulate('click'); + expect(wrapper.find('.appSearchResult__hiddenFieldsToggle').exists()).toBe(true); + wrapper.find('.appSearchResult__hiddenFieldsToggle').simulate('click'); }); - it('renders a collapse button', () => { - expect(wrapper.find('[data-test-subj="CollapseResult"]').exists()).toBe(true); + it('renders correct toggle text', () => { + expect(wrapper.find('.appSearchResult__hiddenFieldsToggle').text()).toEqual( + 'Hide additional fields' + ); }); - it('does not render an expand button', () => { - expect(wrapper.find('[data-test-subj="ExpandResult"]').exists()).toBe(false); + it('renders a collapse icon', () => { + expect(wrapper.find('[data-test-subj="CollapseResult"]').exists()).toBe(true); }); - it('does not render a hidden fields indicator', () => { - expect(wrapper.find('.appSearchResult__hiddenFieldsIndicator').exists()).toBe(false); + it('does not render an expand icon', () => { + expect(wrapper.find('[data-test-subj="ExpandResult"]').exists()).toBe(false); }); it('shows all fields', () => { @@ -188,23 +195,23 @@ describe('Result', () => { beforeAll(() => { wrapper = shallow(); - expect(wrapper.find('.appSearchResult__actionButton').exists()).toBe(true); - wrapper.find('.appSearchResult__actionButton').simulate('click'); - wrapper.find('.appSearchResult__actionButton').simulate('click'); + expect(wrapper.find('.appSearchResult__hiddenFieldsToggle').exists()).toBe(true); + wrapper.find('.appSearchResult__hiddenFieldsToggle').simulate('click'); + wrapper.find('.appSearchResult__hiddenFieldsToggle').simulate('click'); }); - it('renders a collapse button', () => { - expect(wrapper.find('[data-test-subj="CollapseResult"]').exists()).toBe(false); + it('renders correct toggle text', () => { + expect(wrapper.find('.appSearchResult__hiddenFieldsToggle').text()).toEqual( + 'Show 1 additional field' + ); }); - it('does not render an expand button', () => { - expect(wrapper.find('[data-test-subj="ExpandResult"]').exists()).toBe(true); + it('renders a collapse icon', () => { + expect(wrapper.find('[data-test-subj="CollapseResult"]').exists()).toBe(false); }); - it('renders a hidden fields indicator', () => { - expect(wrapper.find('.appSearchResult__hiddenFieldsIndicator').text()).toEqual( - '1 more fields' - ); + it('does not render an expand icon', () => { + expect(wrapper.find('[data-test-subj="ExpandResult"]').exists()).toBe(true); }); it('shows no more than 5 fields', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.tsx index 11415f5512380..f25eb2a4ba09e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.tsx @@ -5,6 +5,7 @@ */ import React, { useState, useMemo } from 'react'; +import classNames from 'classnames'; import './result.scss'; @@ -20,6 +21,7 @@ import { Schema } from '../../../shared/types'; interface Props { result: ResultType; + isMetaEngine: boolean; showScore?: boolean; shouldLinkToDetailPage?: boolean; schemaForTypeHighlights?: Schema; @@ -29,6 +31,7 @@ const RESULT_CUTOFF = 5; export const Result: React.FC = ({ result, + isMetaEngine, showScore = false, shouldLinkToDetailPage = false, schemaForTypeHighlights, @@ -47,75 +50,91 @@ export const Result: React.FC = ({ if (schemaForTypeHighlights) return schemaForTypeHighlights[fieldName]; }; + const documentLink = getDocumentDetailRoute(resultMeta.engine, resultMeta.id); const conditionallyLinkedArticle = (children: React.ReactNode) => { return shouldLinkToDetailPage ? ( - - {children} + +
    + {children} +
    ) : (
    {children}
    ); }; + const classes = classNames('appSearchResult', { + 'appSearchResult--link': shouldLinkToDetailPage, + }); + return ( {conditionallyLinkedArticle( <> - -
    - {resultFields - .slice(0, isOpen ? resultFields.length : RESULT_CUTOFF) - .map(([field, value]: [string, FieldValue]) => ( - - ))} -
    - {numResults > RESULT_CUTOFF && !isOpen && ( -
    - {i18n.translate('xpack.enterpriseSearch.appSearch.result.numberOfAdditionalFields', { - defaultMessage: '{numberOfAdditionalFields} more fields', - values: { - numberOfAdditionalFields: numResults - RESULT_CUTOFF, - }, - })} -
    - )} + + {resultFields + .slice(0, isOpen ? resultFields.length : RESULT_CUTOFF) + .map(([field, value]: [string, FieldValue]) => ( + + ))} )} {numResults > RESULT_CUTOFF && ( )} +
    + {shouldLinkToDetailPage && ( + + + + + + )} +
    ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result_header.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result_header.test.tsx index 95b77a0aed7bb..4ccebb90eb6fe 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result_header.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result_header.test.tsx @@ -13,57 +13,64 @@ import { ResultHeader } from './result_header'; describe('ResultHeader', () => { const resultMeta = { id: '1', - scopedId: '1', score: 100, engine: 'my-engine', }; it('renders', () => { - const wrapper = shallow(); + const wrapper = shallow( + + ); expect(wrapper.isEmptyRender()).toBe(false); }); it('always renders an id', () => { - const wrapper = shallow(); + const wrapper = shallow( + + ); expect(wrapper.find('[data-test-subj="ResultId"]').prop('value')).toEqual('1'); }); describe('score', () => { it('renders score if showScore is true ', () => { - const wrapper = shallow(); + const wrapper = shallow( + + ); expect(wrapper.find('[data-test-subj="ResultScore"]').prop('value')).toEqual(100); }); it('does not render score if showScore is false', () => { - const wrapper = shallow(); + const wrapper = shallow( + + ); expect(wrapper.find('[data-test-subj="ResultScore"]').exists()).toBe(false); }); }); describe('engine', () => { - it('renders engine name if the ids dont match, which means it is a meta engine', () => { + it('renders engine name if this is a meta engine', () => { const wrapper = shallow( ); expect(wrapper.find('[data-test-subj="ResultEngine"]').prop('value')).toBe('my-engine'); }); - it('does not render an engine name if the ids match, which means it is not a meta engine', () => { + it('does not render an engine if this is not a meta engine', () => { const wrapper = shallow( ); expect(wrapper.find('[data-test-subj="ResultEngine"]').exists()).toBe(false); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result_header.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result_header.tsx index 9b83014d041dd..14e0607e1249a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result_header.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result_header.tsx @@ -13,12 +13,11 @@ import './result_header.scss'; interface Props { showScore: boolean; + isMetaEngine: boolean; resultMeta: ResultMeta; } -export const ResultHeader: React.FC = ({ showScore, resultMeta }) => { - const showEngineLabel: boolean = resultMeta.id !== resultMeta.scopedId; - +export const ResultHeader: React.FC = ({ showScore, resultMeta, isMetaEngine }) => { return (
    {showScore && ( @@ -33,7 +32,7 @@ export const ResultHeader: React.FC = ({ showScore, resultMeta }) => { )}
    - {showEngineLabel && ( + {isMetaEngine && ( = ({ text, children }) => { defaultMessage: 'Hidden text', }); const hiddenText = isHidden ? ( - {text.replace(/./g, '•')} + + {text.replace(/./g, '•')} + ) : ( text ); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/schema/schema_add_field_modal.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/schema/schema_add_field_modal.test.tsx index e10d56ddc09b0..67add0ca94520 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/schema/schema_add_field_modal.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/schema/schema_add_field_modal.test.tsx @@ -41,10 +41,10 @@ describe('SchemaAddFieldModal', () => { expect(wrapper.find(EuiModal)).toHaveLength(1); }); - // No matter what I try I can't get this to actually achieve coverage. it('sets loading state in useEffect', () => { setState(true); - const wrapper = mount(); + const wrapper = mount(); + wrapper.setProps({ ...errors }); const input = wrapper.find(EuiFieldText); expect(input.prop('isLoading')).toEqual(false); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_icon/source_icon.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_icon/source_icon.test.tsx index 4007f7a69f77a..b411d749b8a25 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_icon/source_icon.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_icon/source_icon.test.tsx @@ -24,4 +24,10 @@ describe('SourceIcon', () => { expect(wrapper.find('.wrapped-icon')).toHaveLength(1); }); + + it('renders a full bleed icon', () => { + const wrapper = shallow(); + + expect(wrapper.find(EuiIcon).prop('type')).toEqual('test-file-stub'); + }); }); diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/documents.test.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/documents.test.ts index d5fed4c6f97cb..5f57db40cd7e6 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/documents.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/documents.test.ts @@ -6,7 +6,60 @@ import { MockRouter, mockRequestHandler, mockDependencies } from '../../__mocks__'; -import { registerDocumentRoutes } from './documents'; +import { registerDocumentsRoutes, registerDocumentRoutes } from './documents'; + +describe('documents routes', () => { + describe('POST /api/app_search/engines/{engineName}/documents', () => { + let mockRouter: MockRouter; + + beforeEach(() => { + jest.clearAllMocks(); + mockRouter = new MockRouter({ + method: 'post', + path: '/api/app_search/engines/{engineName}/documents', + payload: 'body', + }); + + registerDocumentsRoutes({ + ...mockDependencies, + router: mockRouter.router, + }); + }); + + it('creates a request to enterprise search', () => { + mockRouter.callRoute({ + params: { engineName: 'some-engine' }, + body: { documents: [{ foo: 'bar' }] }, + }); + + expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ + path: '/as/engines/some-engine/documents/new', + }); + }); + + describe('validates', () => { + it('correctly', () => { + const request = { body: { documents: [{ foo: 'bar' }] } }; + mockRouter.shouldValidate(request); + }); + + it('missing documents', () => { + const request = { body: {} }; + mockRouter.shouldThrow(request); + }); + + it('wrong document type', () => { + const request = { body: { documents: ['test'] } }; + mockRouter.shouldThrow(request); + }); + + it('non-array documents type', () => { + const request = { body: { documents: { foo: 'bar' } } }; + mockRouter.shouldThrow(request); + }); + }); + }); +}); describe('document routes', () => { describe('GET /api/app_search/engines/{engineName}/documents/{documentId}', () => { diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/documents.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/documents.ts index a2f4b323a91aa..60cd64b32479c 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/documents.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/documents.ts @@ -8,6 +8,30 @@ import { schema } from '@kbn/config-schema'; import { RouteDependencies } from '../../plugin'; +export function registerDocumentsRoutes({ + router, + enterpriseSearchRequestHandler, +}: RouteDependencies) { + router.post( + { + path: '/api/app_search/engines/{engineName}/documents', + validate: { + params: schema.object({ + engineName: schema.string(), + }), + body: schema.object({ + documents: schema.arrayOf(schema.object({}, { unknowns: 'allow' })), + }), + }, + }, + async (context, request, response) => { + return enterpriseSearchRequestHandler.createRequest({ + path: `/as/engines/${request.params.engineName}/documents/new`, + })(context, request, response); + } + ); +} + export function registerDocumentRoutes({ router, enterpriseSearchRequestHandler, diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/index.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/index.ts index f64e45c656fa1..67dcbfdc4f4d5 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/index.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/index.ts @@ -9,11 +9,12 @@ import { RouteDependencies } from '../../plugin'; import { registerEnginesRoutes } from './engines'; import { registerCredentialsRoutes } from './credentials'; import { registerSettingsRoutes } from './settings'; -import { registerDocumentRoutes } from './documents'; +import { registerDocumentsRoutes, registerDocumentRoutes } from './documents'; export const registerAppSearchRoutes = (dependencies: RouteDependencies) => { registerEnginesRoutes(dependencies); registerCredentialsRoutes(dependencies); registerSettingsRoutes(dependencies); + registerDocumentsRoutes(dependencies); registerDocumentRoutes(dependencies); }; diff --git a/x-pack/plugins/features/server/__snapshots__/oss_features.test.ts.snap b/x-pack/plugins/features/server/__snapshots__/oss_features.test.ts.snap index 680429a4f5946..b1d7a2d434968 100644 --- a/x-pack/plugins/features/server/__snapshots__/oss_features.test.ts.snap +++ b/x-pack/plugins/features/server/__snapshots__/oss_features.test.ts.snap @@ -73,6 +73,7 @@ Array [ "dashboard", "query", "url", + "background-session", ], "read": Array [ "index-pattern", @@ -83,7 +84,6 @@ Array [ "lens", "map", "tag", - "background-session", ], }, "ui": Array [ @@ -92,6 +92,7 @@ Array [ "showWriteControls", "saveQuery", "createShortUrl", + "storeSearchSession", ], }, "privilegeId": "all", @@ -205,8 +206,8 @@ Array [ "search", "query", "index-pattern", - "background-session", "url", + "background-session", ], "read": Array [], }, @@ -215,6 +216,7 @@ Array [ "save", "saveQuery", "createShortUrl", + "storeSearchSession", ], }, "privilegeId": "all", @@ -557,6 +559,7 @@ Array [ "dashboard", "query", "url", + "background-session", ], "read": Array [ "index-pattern", @@ -567,7 +570,6 @@ Array [ "lens", "map", "tag", - "background-session", ], }, "ui": Array [ @@ -576,6 +578,7 @@ Array [ "showWriteControls", "saveQuery", "createShortUrl", + "storeSearchSession", ], }, "privilegeId": "all", @@ -689,8 +692,8 @@ Array [ "search", "query", "index-pattern", - "background-session", "url", + "background-session", ], "read": Array [], }, @@ -699,6 +702,7 @@ Array [ "save", "saveQuery", "createShortUrl", + "storeSearchSession", ], }, "privilegeId": "all", diff --git a/x-pack/plugins/features/server/oss_features.ts b/x-pack/plugins/features/server/oss_features.ts index 209e26821aedd..c38fdf8b29d12 100644 --- a/x-pack/plugins/features/server/oss_features.ts +++ b/x-pack/plugins/features/server/oss_features.ts @@ -28,7 +28,7 @@ export const buildOSSFeatures = ({ savedObjectTypes, includeTimelion }: BuildOSS app: ['discover', 'kibana'], catalogue: ['discover'], savedObject: { - all: ['search', 'query', 'index-pattern', 'background-session'], + all: ['search', 'query', 'index-pattern'], read: [], }, ui: ['show', 'save', 'saveQuery'], @@ -71,6 +71,33 @@ export const buildOSSFeatures = ({ savedObjectTypes, includeTimelion }: BuildOSS }, ], }, + { + name: i18n.translate('xpack.features.ossFeatures.discoverSearchSessionsFeatureName', { + defaultMessage: 'Store Search Sessions', + }), + privilegeGroups: [ + { + groupType: 'independent', + privileges: [ + { + id: 'store_search_session', + name: i18n.translate( + 'xpack.features.ossFeatures.discoverStoreSearchSessionsPrivilegeName', + { + defaultMessage: 'Store Search Sessions', + } + ), + includeIn: 'all', + savedObject: { + all: ['background-session'], + read: [], + }, + ui: ['storeSearchSession'], + }, + ], + }, + ], + }, ], }, { @@ -156,7 +183,6 @@ export const buildOSSFeatures = ({ savedObjectTypes, includeTimelion }: BuildOSS 'lens', 'map', 'tag', - 'background-session', ], }, ui: ['createNew', 'show', 'showWriteControls', 'saveQuery'], @@ -210,6 +236,33 @@ export const buildOSSFeatures = ({ savedObjectTypes, includeTimelion }: BuildOSS }, ], }, + { + name: i18n.translate('xpack.features.ossFeatures.dashboardSearchSessionsFeatureName', { + defaultMessage: 'Store Search Sessions', + }), + privilegeGroups: [ + { + groupType: 'independent', + privileges: [ + { + id: 'store_search_session', + name: i18n.translate( + 'xpack.features.ossFeatures.dashboardStoreSearchSessionsPrivilegeName', + { + defaultMessage: 'Store Search Sessions', + } + ), + includeIn: 'all', + savedObject: { + all: ['background-session'], + read: [], + }, + ui: ['storeSearchSession'], + }, + ], + }, + ], + }, ], }, { diff --git a/x-pack/plugins/fleet/common/constants/epm.ts b/x-pack/plugins/fleet/common/constants/epm.ts index b94c2cd12cd5f..5ba4de914c724 100644 --- a/x-pack/plugins/fleet/common/constants/epm.ts +++ b/x-pack/plugins/fleet/common/constants/epm.ts @@ -3,8 +3,8 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - export const PACKAGES_SAVED_OBJECT_TYPE = 'epm-packages'; +export const ASSETS_SAVED_OBJECT_TYPE = 'epm-packages-assets'; export const INDEX_PATTERN_SAVED_OBJECT_TYPE = 'index-pattern'; export const MAX_TIME_COMPLETE_INSTALL = 60000; diff --git a/x-pack/plugins/fleet/common/openapi/bundled.json b/x-pack/plugins/fleet/common/openapi/bundled.json index 1d00855de8935..e9b11a2f5ac83 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.json +++ b/x-pack/plugins/fleet/common/openapi/bundled.json @@ -1274,6 +1274,15 @@ "put": { "summary": "PackagePolicies - Update", "operationId": "put-packagePolicies-packagePolicyId", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/update_package_policy" + } + } + } + }, "responses": { "200": { "description": "OK", @@ -2077,6 +2086,22 @@ "download", "path" ] + }, + "update_package_policy": { + "title": "UpdatePackagePolicy", + "allOf": [ + { + "type": "object", + "properties": { + "version": { + "type": "string" + } + } + }, + { + "$ref": "#/components/schemas/new_package_policy" + } + ] } } }, diff --git a/x-pack/plugins/fleet/common/openapi/bundled.yaml b/x-pack/plugins/fleet/common/openapi/bundled.yaml index 9ab85ab2b8232..05b5b239dc980 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.yaml +++ b/x-pack/plugins/fleet/common/openapi/bundled.yaml @@ -789,6 +789,11 @@ paths: put: summary: PackagePolicies - Update operationId: put-packagePolicies-packagePolicyId + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/update_package_policy' responses: '200': description: OK @@ -1323,5 +1328,13 @@ components: - format_version - download - path + update_package_policy: + title: UpdatePackagePolicy + allOf: + - type: object + properties: + version: + type: string + - $ref: '#/components/schemas/new_package_policy' security: - basicAuth: [] diff --git a/x-pack/plugins/fleet/common/openapi/components/schemas/update_package_policy.yaml b/x-pack/plugins/fleet/common/openapi/components/schemas/update_package_policy.yaml new file mode 100644 index 0000000000000..054a0e1a48be0 --- /dev/null +++ b/x-pack/plugins/fleet/common/openapi/components/schemas/update_package_policy.yaml @@ -0,0 +1,7 @@ +title: UpdatePackagePolicy +allOf: + - type: object + properties: + version: + type: string + - $ref: ./new_package_policy.yaml diff --git a/x-pack/plugins/fleet/common/openapi/paths/package_policies@{package_policy_id}.yaml b/x-pack/plugins/fleet/common/openapi/paths/package_policies@{package_policy_id}.yaml index 3b177be3d032e..4e0315556b614 100644 --- a/x-pack/plugins/fleet/common/openapi/paths/package_policies@{package_policy_id}.yaml +++ b/x-pack/plugins/fleet/common/openapi/paths/package_policies@{package_policy_id}.yaml @@ -23,6 +23,11 @@ parameters: put: summary: PackagePolicies - Update operationId: put-packagePolicies-packagePolicyId + requestBody: + content: + application/json: + schema: + $ref: ../components/schemas/update_package_policy.yaml responses: '200': description: OK diff --git a/x-pack/plugins/fleet/common/types/models/epm.ts b/x-pack/plugins/fleet/common/types/models/epm.ts index a9893b170492f..77625e48dbc96 100644 --- a/x-pack/plugins/fleet/common/types/models/epm.ts +++ b/x-pack/plugins/fleet/common/types/models/epm.ts @@ -8,6 +8,7 @@ // TODO: Update when https://github.com/elastic/kibana/issues/53021 is closed import { SavedObject, SavedObjectAttributes, SavedObjectReference } from 'src/core/public'; import { + ASSETS_SAVED_OBJECT_TYPE, agentAssetTypes, dataTypes, defaultPackages, @@ -268,6 +269,7 @@ export type PackageInfo = export interface Installation extends SavedObjectAttributes { installed_kibana: KibanaAssetReference[]; installed_es: EsAssetReference[]; + package_assets: PackageAssetReference[]; es_index_patterns: Record; name: string; version: string; @@ -297,6 +299,10 @@ export type EsAssetReference = Pick & { type: ElasticsearchAssetType; }; +export type PackageAssetReference = Pick & { + type: typeof ASSETS_SAVED_OBJECT_TYPE; +}; + export type RequiredPackage = typeof requiredPackages; export type DefaultPackages = typeof defaultPackages; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/linked_agent_count.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/linked_agent_count.tsx new file mode 100644 index 0000000000000..bbe7f1254a140 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/components/linked_agent_count.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { memo } from 'react'; +import { EuiLink, EuiLinkAnchorProps } from '@elastic/eui'; +import { useLink } from '../hooks'; +import { AGENT_SAVED_OBJECT_TYPE } from '../constants'; + +/** + * Displays the provided `count` number as a link to the Agents list if it is greater than zero + */ +export const LinkedAgentCount = memo< + Omit & { count: number; agentPolicyId: string } +>(({ count, agentPolicyId, ...otherEuiLinkProps }) => { + const { getHref } = useLink(); + return count > 0 ? ( + + {count} + + ) : ( + + {count} + + ); +}); diff --git a/x-pack/plugins/fleet/public/applications/fleet/hooks/use_fleet_status.tsx b/x-pack/plugins/fleet/public/applications/fleet/hooks/use_fleet_status.tsx index 18bcb4539c740..2bb328a51c60a 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/hooks/use_fleet_status.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/hooks/use_fleet_status.tsx @@ -13,6 +13,7 @@ interface FleetStatusState { enabled: boolean; isLoading: boolean; isReady: boolean; + error?: Error; missingRequirements?: GetFleetStatusResponse['missing_requirements']; } @@ -44,7 +45,7 @@ export const FleetStatusProvider: React.FC = ({ children }) => { missingRequirements: res.data?.missing_requirements, })); } catch (error) { - setState((s) => ({ ...s, isLoading: true })); + setState((s) => ({ ...s, isLoading: false, error })); } } useEffect(() => { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/index.ts b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/index.ts index 1ec43f4df8c8e..ca76b65518ebe 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/index.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/index.ts @@ -8,7 +8,7 @@ export { AgentPolicyCopyProvider } from './agent_policy_copy_provider'; export { AgentPolicyDeleteProvider } from './agent_policy_delete_provider'; export { PackagePolicyDeleteProvider } from './package_policy_delete_provider'; export { AgentPolicyYamlFlyout } from './agent_policy_yaml_flyout'; -export { LinkedAgentCount } from './linked_agent_count'; +export { LinkedAgentCount } from '../../../components/linked_agent_count'; export { ConfirmDeployAgentPolicyModal } from './confirm_deploy_modal'; export { DangerEuiContextMenuItem } from './danger_eui_context_menu_item'; export { AgentPolicyActionMenu } from './actions_menu'; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/linked_agent_count.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/linked_agent_count.tsx deleted file mode 100644 index c602f492f74c6..0000000000000 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/linked_agent_count.tsx +++ /dev/null @@ -1,35 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { memo } from 'react'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiLink } from '@elastic/eui'; -import { useLink } from '../../../hooks'; -import { AGENT_SAVED_OBJECT_TYPE } from '../../../constants'; - -export const LinkedAgentCount = memo<{ count: number; agentPolicyId: string }>( - ({ count, agentPolicyId }) => { - const { getHref } = useLink(); - const displayValue = ( - - ); - return count > 0 ? ( - - {displayValue} - - ) : ( - displayValue - ); - } -); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/layout.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/layout.tsx index cac133acd4d2d..19f6be3db51b0 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/layout.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/layout.tsx @@ -51,8 +51,8 @@ export const CreatePackagePolicyPageLayout: React.FunctionComponent<{ -

    - {from === 'edit' ? ( +

    + {from === 'edit' || from === 'package-edit' ? ( -

    +

    ); - }, [from, packageInfo]); + }, [dataTestSubj, from, packageInfo]); const pageDescription = useMemo(() => { return from === 'edit' || from === 'package-edit' ? ( diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx index 758131a9a4b7e..8c6163578617c 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx @@ -4,9 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; import { HashRouter as Router, Route, Switch, Redirect } from 'react-router-dom'; import { PAGE_ROUTING_PATHS } from '../../constants'; -import { Loading } from '../../components'; +import { Loading, Error } from '../../components'; import { useConfig, useFleetStatus, useBreadcrumbs, useCapabilities } from '../../hooks'; import { AgentListPage } from './agent_list_page'; import { SetupPage } from './setup_page'; @@ -14,6 +15,7 @@ import { AgentDetailsPage } from './agent_details_page'; import { NoAccessPage } from './error_pages/no_access'; import { EnrollmentTokenListPage } from './enrollment_token_list_page'; import { ListLayout } from './components/list_layout'; +import { WithoutHeaderLayout } from '../../layouts'; export const FleetApp: React.FunctionComponent = () => { useBreadcrumbs('fleet'); @@ -27,6 +29,22 @@ export const FleetApp: React.FunctionComponent = () => { return ; } + if (fleetStatus.error) { + return ( + + + } + error={fleetStatus.error} + /> + + ); + } + if (fleetStatus.isReady === false) { return ( { it('should link to integration policy detail when an integration policy is clicked', async () => { await mockedApi.waitForApi(); - const firstPolicy = renderResult.getByTestId('integrationNameLink') as HTMLAnchorElement; + const firstPolicy = renderResult.getAllByTestId( + 'integrationNameLink' + )[0] as HTMLAnchorElement; expect(firstPolicy.href).toEqual( 'http://localhost/mock/app/fleet#/integrations/edit-integration/e8a37031-2907-44f6-89d2-98bd493f60dc' ); }); + + it('should NOT show link for agent count if it is zero', async () => { + await mockedApi.waitForApi(); + const firstRowAgentCount = renderResult.getAllByTestId('rowAgentCount')[0]; + expect(firstRowAgentCount.textContent).toEqual('0'); + expect(firstRowAgentCount.tagName).not.toEqual('A'); + }); + + it('should show link for agent count if greater than zero', async () => { + await mockedApi.waitForApi(); + const secondRowAgentCount = renderResult.getAllByTestId('rowAgentCount')[1]; + expect(secondRowAgentCount.textContent).toEqual('100'); + expect(secondRowAgentCount.tagName).toEqual('A'); + }); }); }); @@ -522,8 +538,87 @@ On Windows, the module was tested with Nginx installed from the Chocolatey repos updated_at: '2020-12-09T13:46:31.013Z', updated_by: 'elastic', }, + { + id: 'e3t37031-2907-44f6-89d2-5555555555', + version: 'WrrrMiwxXQ==', + name: 'nginx-2', + description: '', + namespace: 'default', + policy_id: '125c1b70-3976-11eb-ad1c-3baa423085y6', + enabled: true, + output_id: '', + inputs: [ + { + type: 'logfile', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'logs', dataset: 'nginx.access' }, + vars: { paths: { value: ['/var/log/nginx/access.log*'], type: 'text' } }, + id: 'logfile-nginx.access-e8a37031-2907-44f6-89d2-98bd493f60dc', + compiled_stream: { + paths: ['/var/log/nginx/access.log*'], + exclude_files: ['.gz$'], + processors: [{ add_locale: null }], + }, + }, + { + enabled: true, + data_stream: { type: 'logs', dataset: 'nginx.error' }, + vars: { paths: { value: ['/var/log/nginx/error.log*'], type: 'text' } }, + id: 'logfile-nginx.error-e8a37031-2907-44f6-89d2-98bd493f60dc', + compiled_stream: { + paths: ['/var/log/nginx/error.log*'], + exclude_files: ['.gz$'], + multiline: { + pattern: '^\\d{4}\\/\\d{2}\\/\\d{2} ', + negate: true, + match: 'after', + }, + processors: [{ add_locale: null }], + }, + }, + { + enabled: false, + data_stream: { type: 'logs', dataset: 'nginx.ingress_controller' }, + vars: { paths: { value: ['/var/log/nginx/ingress.log*'], type: 'text' } }, + id: 'logfile-nginx.ingress_controller-e8a37031-2907-44f6-89d2-98bd493f60dc', + }, + ], + }, + { + type: 'nginx/metrics', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'nginx.stubstatus' }, + vars: { + period: { value: '10s', type: 'text' }, + server_status_path: { value: '/nginx_status', type: 'text' }, + }, + id: 'nginx/metrics-nginx.stubstatus-e8a37031-2907-44f6-89d2-98bd493f60dc', + compiled_stream: { + metricsets: ['stubstatus'], + hosts: ['http://127.0.0.1:80'], + period: '10s', + server_status_path: '/nginx_status', + }, + }, + ], + vars: { hosts: { value: ['http://127.0.0.1:80'], type: 'text' } }, + }, + ], + package: { name: 'nginx', title: 'Nginx', version: '0.3.7' }, + revision: 3, + created_at: '2020-12-09T13:46:31.013Z', + created_by: 'elastic', + updated_at: '2020-12-09T13:46:31.013Z', + updated_by: 'elastic', + }, ], - total: 1, + total: 2, page: 1, perPage: 20, }; @@ -548,8 +643,22 @@ On Windows, the module was tested with Nginx installed from the Chocolatey repos updated_by: 'elastic', agents: 0, }, + { + id: '125c1b70-3976-11eb-ad1c-3baa423085y6', + name: 'EU Healthy agents', + namespace: 'default', + description: 'Protect EU from COVID', + status: 'active', + package_policies: ['e8a37031-2907-44f6-89d2-98bd493f60cd'], + is_default: false, + monitoring_enabled: ['logs', 'metrics'], + revision: 2, + updated_at: '2020-12-09T13:46:31.840Z', + updated_by: 'elastic', + agents: 100, + }, ], - total: 1, + total: 2, page: 1, perPage: 100, }; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/epm/screens/detail/package_policies_panel.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/epm/screens/detail/package_policies_panel.tsx index 4d8cb5a16034f..c740adc4201de 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/epm/screens/detail/package_policies_panel.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/epm/screens/detail/package_policies_panel.tsx @@ -17,10 +17,7 @@ import { FormattedRelative, FormattedMessage } from '@kbn/i18n/react'; import { useGetPackageInstallStatus } from '../../hooks'; import { InstallStatus } from '../../../../types'; import { useLink } from '../../../../hooks'; -import { - AGENT_SAVED_OBJECT_TYPE, - PACKAGE_POLICY_SAVED_OBJECT_TYPE, -} from '../../../../../../../common/constants'; +import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../../../../../../../common/constants'; import { useUrlPagination } from '../../../../hooks'; import { PackagePolicyAndAgentPolicy, @@ -28,6 +25,7 @@ import { } from './use_package_policies_with_agent_policy'; import { LinkAndRevision, LinkAndRevisionProps } from '../../../../components'; import { Persona } from './persona'; +import { LinkedAgentCount } from '../../../../components/linked_agent_count'; const IntegrationDetailsLink = memo<{ packagePolicy: PackagePolicyAndAgentPolicy['packagePolicy']; @@ -66,22 +64,6 @@ const AgentPolicyDetailLink = memo<{ ); }); -const PolicyAgentListLink = memo<{ agentPolicyId: string; children: ReactNode }>( - ({ agentPolicyId, children }) => { - const { getHref } = useLink(); - return ( - - {children} - - ); - } -); - interface PackagePoliciesPanelProps { name: string; version: string; @@ -156,9 +138,12 @@ export const PackagePoliciesPanel = ({ name, version }: PackagePoliciesPanelProp width: '8ch', render({ packagePolicy, agentPolicy }: PackagePolicyAndAgentPolicy) { return ( - - {agentPolicy?.agents ?? 0} - + ); }, }, diff --git a/x-pack/plugins/fleet/server/constants/index.ts b/x-pack/plugins/fleet/server/constants/index.ts index 5d00b96634214..dbf2fbc362a45 100644 --- a/x-pack/plugins/fleet/server/constants/index.ts +++ b/x-pack/plugins/fleet/server/constants/index.ts @@ -40,6 +40,7 @@ export { PACKAGE_POLICY_SAVED_OBJECT_TYPE, OUTPUT_SAVED_OBJECT_TYPE, PACKAGES_SAVED_OBJECT_TYPE, + ASSETS_SAVED_OBJECT_TYPE, INDEX_PATTERN_SAVED_OBJECT_TYPE, ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, GLOBAL_SETTINGS_SAVED_OBJECT_TYPE, diff --git a/x-pack/plugins/fleet/server/routes/epm/handlers.ts b/x-pack/plugins/fleet/server/routes/epm/handlers.ts index cfcde99541f13..9ccf60dc80a5f 100644 --- a/x-pack/plugins/fleet/server/routes/epm/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/epm/handlers.ts @@ -41,12 +41,13 @@ import { removeInstallation, getLimitedPackages, getInstallationObject, + getInstallation, } from '../../services/epm/packages'; import { defaultIngestErrorHandler, ingestErrorToResponseOptions } from '../../errors'; import { splitPkgKey } from '../../services/epm/registry'; import { licenseService } from '../../services'; import { getArchiveEntry } from '../../services/epm/archive/cache'; -import { bufferToStream } from '../../services/epm/streams'; +import { getAsset } from '../../services/epm/archive/storage'; export const getCategoriesHandler: RequestHandler< undefined, @@ -107,32 +108,51 @@ export const getFileHandler: RequestHandler { + const { path, buffer, name, version, installSource } = opts; + const fileExt = extname(path); + const contentType = mime.lookup(fileExt); + const mediaType = mime.contentType(contentType || fileExt); + // can use to create a data URL like `data:${mediaType};base64,${base64Data}` + + const bufferIsBinary = await isBinaryFile(buffer); + const dataUtf8 = bufferIsBinary ? '' : buffer.toString('utf8'); + const dataBase64 = bufferIsBinary ? buffer.toString('base64') : ''; + + // validation: filesize? asset type? anything else + if (dataUtf8.length > MAX_ES_ASSET_BYTES) { + throw new Error(`File at ${path} is larger than maximum allowed size of ${MAX_ES_ASSET_BYTES}`); + } + + if (dataBase64.length > MAX_ES_ASSET_BYTES) { + throw new Error( + `After base64 encoding file at ${path} is larger than maximum allowed size of ${MAX_ES_ASSET_BYTES}` + ); + } + + return { + package_name: name, + package_version: version, + install_source: installSource, + asset_path: path, + media_type: mediaType || '', + data_utf8: dataUtf8, + data_base64: dataBase64, + }; +} + +export async function removeArchiveEntries(opts: { + savedObjectsClient: SavedObjectsClientContract; + refs: PackageAssetReference[]; +}) { + const { savedObjectsClient, refs } = opts; + const results = await Promise.all( + refs.map((ref) => savedObjectsClient.delete(ASSETS_SAVED_OBJECT_TYPE, ref.id)) + ); + return results; +} + +export async function saveArchiveEntries(opts: { + savedObjectsClient: SavedObjectsClientContract; + paths: string[]; + packageInfo: InstallablePackage; + installSource: InstallSource; +}) { + const { savedObjectsClient, paths, packageInfo, installSource } = opts; + const bulkBody = await Promise.all( + paths.map((path) => { + const buffer = getArchiveEntry(path); + if (!buffer) throw new Error(`Could not find ArchiveEntry at ${path}`); + const { name, version } = packageInfo; + return archiveEntryToBulkCreateObject({ path, buffer, name, version, installSource }); + }) + ); + + const results = await savedObjectsClient.bulkCreate(bulkBody); + return results; +} + +export async function archiveEntryToBulkCreateObject(opts: { + path: string; + buffer: Buffer; + name: string; + version: string; + installSource: InstallSource; +}): Promise> { + const { path, buffer, name, version, installSource } = opts; + const doc = await archiveEntryToESDocument({ path, buffer, name, version, installSource }); + return { + id: assetPathToObjectId(doc.asset_path), + type: ASSETS_SAVED_OBJECT_TYPE, + attributes: doc, + }; +} + +export async function getAsset(opts: { + savedObjectsClient: SavedObjectsClientContract; + path: string; +}) { + const { savedObjectsClient, path } = opts; + const assetSavedObject = await savedObjectsClient.get( + ASSETS_SAVED_OBJECT_TYPE, + assetPathToObjectId(path) + ); + const storedAsset = assetSavedObject?.attributes; + if (!storedAsset) { + return; + } + + return storedAsset; +} diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/index.test.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/index.test.ts index 78aa17da5030c..0634e3b25f89e 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/index.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/index.test.ts @@ -20,3 +20,18 @@ test('getBaseName', () => { const name = getRegistryDataStreamAssetBaseName(dataStream); expect(name).toStrictEqual('logs-nginx.access'); }); + +test('getBaseName for hidden index', () => { + const dataStream: RegistryDataStream = { + dataset: 'nginx.access', + title: 'Nginx Acess Logs', + release: 'beta', + type: 'logs', + ingest_pipeline: 'default', + package: 'nginx', + path: 'access', + hidden: true, + }; + const name = getRegistryDataStreamAssetBaseName(dataStream); + expect(name).toStrictEqual('.logs-nginx.access'); +}); diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/index.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/index.ts index 17cd28cc8a081..a7647cc95cbaa 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/index.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/index.ts @@ -11,5 +11,6 @@ import { RegistryDataStream } from '../../../types'; * {type}-{dataset} */ export function getRegistryDataStreamAssetBaseName(dataStream: RegistryDataStream): string { - return `${dataStream.type}-${dataStream.dataset}`; + const baseName = `${dataStream.type}-${dataStream.dataset}`; + return dataStream.hidden ? `.${baseName}` : baseName; } diff --git a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts index c7500a9cfeaf6..c0e2fcb12bcf8 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts @@ -5,7 +5,13 @@ */ import { SavedObject, SavedObjectsClientContract } from 'src/core/server'; -import { InstallablePackage, InstallSource, MAX_TIME_COMPLETE_INSTALL } from '../../../../common'; +import { + InstallablePackage, + InstallSource, + PackageAssetReference, + MAX_TIME_COMPLETE_INSTALL, + ASSETS_SAVED_OBJECT_TYPE, +} from '../../../../common'; import { PACKAGES_SAVED_OBJECT_TYPE } from '../../../constants'; import { AssetReference, @@ -24,6 +30,7 @@ import { deleteKibanaSavedObjectsAssets } from './remove'; import { installTransform } from '../elasticsearch/transform/install'; import { createInstallation, saveKibanaAssetsRefs, updateVersion } from './install'; import { installIlmForDataStream } from '../elasticsearch/datastream_ilm/install'; +import { saveArchiveEntries } from '../archive/storage'; import { ConcurrentInstallOperationError } from '../../../errors'; // this is only exported for testing @@ -188,11 +195,26 @@ export async function _installPackage({ if (installKibanaAssetsError) throw installKibanaAssetsError; await Promise.all([installKibanaAssetsPromise, installIndexPatternPromise]); + const packageAssetResults = await saveArchiveEntries({ + savedObjectsClient, + paths, + packageInfo, + installSource, + }); + const packageAssetRefs: PackageAssetReference[] = packageAssetResults.saved_objects.map( + (result) => ({ + id: result.id, + type: ASSETS_SAVED_OBJECT_TYPE, + }) + ); + // update to newly installed version when all assets are successfully installed if (installedPkg) await updateVersion(savedObjectsClient, pkgName, pkgVersion); + await savedObjectsClient.update(PACKAGES_SAVED_OBJECT_TYPE, pkgName, { install_version: pkgVersion, install_status: 'installed', + package_assets: packageAssetRefs, }); return [ diff --git a/x-pack/plugins/fleet/server/services/epm/packages/ensure_installed_default_packages.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/ensure_installed_default_packages.test.ts index 4ad6fc96218de..fe7b8be23b03b 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/ensure_installed_default_packages.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/ensure_installed_default_packages.test.ts @@ -43,6 +43,7 @@ const mockInstallation: SavedObject = { id: 'test-pkg', installed_kibana: [{ type: KibanaSavedObjectType.dashboard, id: 'dashboard-1' }], installed_es: [{ type: ElasticsearchAssetType.ingestPipeline, id: 'pipeline' }], + package_assets: [], es_index_patterns: { pattern: 'pattern-name' }, name: 'test package', version: '1.0.0', diff --git a/x-pack/plugins/fleet/server/services/epm/packages/get_install_type.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/get_install_type.test.ts index a41511260c6e7..2dcfc7949d5e5 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/get_install_type.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/get_install_type.test.ts @@ -15,6 +15,7 @@ const mockInstallation: SavedObject = { id: 'test-pkg', installed_kibana: [{ type: KibanaSavedObjectType.dashboard, id: 'dashboard-1' }], installed_es: [{ type: ElasticsearchAssetType.ingestPipeline, id: 'pipeline' }], + package_assets: [], es_index_patterns: { pattern: 'pattern-name' }, name: 'test packagek', version: '1.0.0', @@ -32,6 +33,7 @@ const mockInstallationUpdateFail: SavedObject = { id: 'test-pkg', installed_kibana: [{ type: KibanaSavedObjectType.dashboard, id: 'dashboard-1' }], installed_es: [{ type: ElasticsearchAssetType.ingestPipeline, id: 'pipeline' }], + package_assets: [], es_index_patterns: { pattern: 'pattern-name' }, name: 'test packagek', version: '1.0.0', diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.ts index 48dd589dd0b8f..176bcf1381674 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts @@ -379,6 +379,7 @@ export async function createInstallation(options: { { installed_kibana: [], installed_es: [], + package_assets: [], es_index_patterns: toSaveESIndexPatterns, name: pkgName, version: pkgVersion, diff --git a/x-pack/plugins/fleet/server/services/epm/packages/remove.ts b/x-pack/plugins/fleet/server/services/epm/packages/remove.ts index 0b4a0faddf0cc..331b6bfa882da 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/remove.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/remove.ts @@ -24,6 +24,7 @@ import { packagePolicyService, appContextService } from '../..'; import { splitPkgKey } from '../registry'; import { deletePackageCache } from '../archive'; import { deleteIlms } from '../elasticsearch/datastream_ilm/remove'; +import { removeArchiveEntries } from '../archive/storage'; export async function removeInstallation(options: { savedObjectsClient: SavedObjectsClientContract; @@ -49,7 +50,7 @@ export async function removeInstallation(options: { `unable to remove package with existing package policy(s) in use by agent(s)` ); - // Delete the installed assets + // Delete the installed assets. Don't include installation.package_assets. Those are irrelevant to users const installedAssets = [...installation.installed_kibana, ...installation.installed_es]; await deleteAssets(installation, savedObjectsClient, callCluster); @@ -69,6 +70,8 @@ export async function removeInstallation(options: { version: pkgVersion, }); + await removeArchiveEntries({ savedObjectsClient, refs: installation.package_assets }); + // successful delete's in SO client return {}. return something more useful return installedAssets; } diff --git a/x-pack/plugins/fleet/server/services/setup.ts b/x-pack/plugins/fleet/server/services/setup.ts index f514f1ecb9ae6..c37eed1910883 100644 --- a/x-pack/plugins/fleet/server/services/setup.ts +++ b/x-pack/plugins/fleet/server/services/setup.ts @@ -137,7 +137,16 @@ export async function setupFleet( cluster: ['monitor', 'manage_api_key'], indices: [ { - names: ['logs-*', 'metrics-*', 'traces-*', '.ds-logs-*', '.ds-metrics-*', '.ds-traces-*'], + names: [ + 'logs-*', + 'metrics-*', + 'traces-*', + '.ds-logs-*', + '.ds-metrics-*', + '.ds-traces-*', + '.logs-endpoint.diagnostic.collection-*', + '.ds-.logs-endpoint.diagnostic.collection-*', + ], privileges: ['write', 'create_index', 'indices:admin/auto_create'], }, ], diff --git a/x-pack/plugins/infra/common/alerting/logs/log_threshold/types.ts b/x-pack/plugins/infra/common/alerting/logs/log_threshold/types.ts index c505a234c7b2b..5f2e355ca3a47 100644 --- a/x-pack/plugins/infra/common/alerting/logs/log_threshold/types.ts +++ b/x-pack/plugins/infra/common/alerting/logs/log_threshold/types.ts @@ -105,29 +105,38 @@ const ThresholdRT = rt.type({ export type Threshold = rt.TypeOf; -export const CriterionRT = rt.type({ +export const criterionRT = rt.type({ field: rt.string, comparator: ComparatorRT, value: rt.union([rt.string, rt.number]), }); +export type Criterion = rt.TypeOf; -export type Criterion = rt.TypeOf; -export const criteriaRT = rt.array(CriterionRT); -export type Criteria = rt.TypeOf; +export const partialCriterionRT = rt.partial(criterionRT.props); +export type PartialCriterion = rt.TypeOf; -export const countCriteriaRT = criteriaRT; +export const countCriteriaRT = rt.array(criterionRT); export type CountCriteria = rt.TypeOf; -export const ratioCriteriaRT = rt.tuple([criteriaRT, criteriaRT]); +export const partialCountCriteriaRT = rt.array(partialCriterionRT); +export type PartialCountCriteria = rt.TypeOf; + +export const ratioCriteriaRT = rt.tuple([countCriteriaRT, countCriteriaRT]); export type RatioCriteria = rt.TypeOf; -export const TimeUnitRT = rt.union([ +export const partialRatioCriteriaRT = rt.tuple([partialCountCriteriaRT, partialCountCriteriaRT]); +export type PartialRatioCriteria = rt.TypeOf; + +export const partialCriteriaRT = rt.union([partialCountCriteriaRT, partialRatioCriteriaRT]); +export type PartialCriteria = rt.TypeOf; + +export const timeUnitRT = rt.union([ rt.literal('s'), rt.literal('m'), rt.literal('h'), rt.literal('d'), ]); -export type TimeUnit = rt.TypeOf; +export type TimeUnit = rt.TypeOf; export const timeSizeRT = rt.number; export const groupByRT = rt.array(rt.string); @@ -136,15 +145,18 @@ const RequiredAlertParamsRT = rt.type({ // NOTE: "count" would be better named as "threshold", but this would require a // migration of encrypted saved objects, so we'll keep "count" until it's problematic. count: ThresholdRT, - timeUnit: TimeUnitRT, + timeUnit: timeUnitRT, timeSize: timeSizeRT, }); +const partialRequiredAlertParamsRT = rt.partial(RequiredAlertParamsRT.props); +export type PartialRequiredAlertParams = rt.TypeOf; + const OptionalAlertParamsRT = rt.partial({ groupBy: groupByRT, }); -export const alertParamsRT = rt.intersection([ +export const countAlertParamsRT = rt.intersection([ rt.type({ criteria: countCriteriaRT, ...RequiredAlertParamsRT.props, @@ -153,8 +165,18 @@ export const alertParamsRT = rt.intersection([ ...OptionalAlertParamsRT.props, }), ]); +export type CountAlertParams = rt.TypeOf; -export type CountAlertParams = rt.TypeOf; +export const partialCountAlertParamsRT = rt.intersection([ + rt.type({ + criteria: partialCountCriteriaRT, + ...RequiredAlertParamsRT.props, + }), + rt.partial({ + ...OptionalAlertParamsRT.props, + }), +]); +export type PartialCountAlertParams = rt.TypeOf; export const ratioAlertParamsRT = rt.intersection([ rt.type({ @@ -165,13 +187,29 @@ export const ratioAlertParamsRT = rt.intersection([ ...OptionalAlertParamsRT.props, }), ]); - export type RatioAlertParams = rt.TypeOf; -export const AlertParamsRT = rt.union([alertParamsRT, ratioAlertParamsRT]); -export type AlertParams = rt.TypeOf; +export const partialRatioAlertParamsRT = rt.intersection([ + rt.type({ + criteria: partialRatioCriteriaRT, + ...RequiredAlertParamsRT.props, + }), + rt.partial({ + ...OptionalAlertParamsRT.props, + }), +]); +export type PartialRatioAlertParams = rt.TypeOf; + +export const alertParamsRT = rt.union([countAlertParamsRT, ratioAlertParamsRT]); +export type AlertParams = rt.TypeOf; + +export const partialAlertParamsRT = rt.union([ + partialCountAlertParamsRT, + partialRatioAlertParamsRT, +]); +export type PartialAlertParams = rt.TypeOf; -export const isRatioAlert = (criteria: AlertParams['criteria']): criteria is RatioCriteria => { +export const isRatioAlert = (criteria: PartialCriteria): criteria is PartialRatioCriteria => { return criteria.length > 0 && Array.isArray(criteria[0]) ? true : false; }; @@ -179,11 +217,13 @@ export const isRatioAlertParams = (params: AlertParams): params is RatioAlertPar return isRatioAlert(params.criteria); }; -export const getNumerator = (criteria: RatioCriteria): Criteria => { +export const getNumerator = (criteria: C): C[0] => { return criteria[0]; }; -export const getDenominator = (criteria: RatioCriteria): Criteria => { +export const getDenominator = ( + criteria: C +): C[1] => { return criteria[1]; }; diff --git a/x-pack/plugins/infra/common/http_api/log_alerts/chart_preview_data.ts b/x-pack/plugins/infra/common/http_api/log_alerts/chart_preview_data.ts index 3226287d4cbde..90547e6812225 100644 --- a/x-pack/plugins/infra/common/http_api/log_alerts/chart_preview_data.ts +++ b/x-pack/plugins/infra/common/http_api/log_alerts/chart_preview_data.ts @@ -6,8 +6,8 @@ import * as rt from 'io-ts'; import { - criteriaRT, - TimeUnitRT, + countCriteriaRT, + timeUnitRT, timeSizeRT, groupByRT, } from '../../alerting/logs/log_threshold/types'; @@ -42,8 +42,8 @@ export type GetLogAlertsChartPreviewDataSuccessResponsePayload = rt.TypeOf< export const getLogAlertsChartPreviewDataAlertParamsSubsetRT = rt.intersection([ rt.type({ - criteria: criteriaRT, - timeUnit: TimeUnitRT, + criteria: countCriteriaRT, + timeUnit: timeUnitRT, timeSize: timeSizeRT, }), rt.partial({ diff --git a/x-pack/plugins/infra/common/utility_types.ts b/x-pack/plugins/infra/common/utility_types.ts index 93fc9b729ca74..6bd784fed9308 100644 --- a/x-pack/plugins/infra/common/utility_types.ts +++ b/x-pack/plugins/infra/common/utility_types.ts @@ -43,3 +43,6 @@ export type DeepPartial = T extends any[] interface DeepPartialArray extends Array> {} type DeepPartialObject = { [P in keyof T]+?: DeepPartial }; + +export type ObjectEntry = [keyof T, T[keyof T]]; +export type ObjectEntries = Array>; diff --git a/x-pack/plugins/infra/public/alerting/inventory/index.ts b/x-pack/plugins/infra/public/alerting/inventory/index.ts index 13ce43f77c8b0..da85f363b16ec 100644 --- a/x-pack/plugins/infra/public/alerting/inventory/index.ts +++ b/x-pack/plugins/infra/public/alerting/inventory/index.ts @@ -5,13 +5,21 @@ */ import { i18n } from '@kbn/i18n'; import React from 'react'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID } from '../../../server/lib/alerting/inventory_metric_threshold/types'; +import { + InventoryMetricConditions, + METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID, + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../server/lib/alerting/inventory_metric_threshold/types'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types'; +import { AlertTypeParams } from '../../../../alerts/common'; import { validateMetricThreshold } from './components/validation'; -export function createInventoryMetricAlertType(): AlertTypeModel { +interface InventoryMetricAlertTypeParams extends AlertTypeParams { + criteria: InventoryMetricConditions[]; +} + +export function createInventoryMetricAlertType(): AlertTypeModel { return { id: METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID, description: i18n.translate('xpack.infra.metrics.inventory.alertFlyout.alertDescription', { diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_dropdown.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_dropdown.tsx index b8eb73b99f45e..0744984a52eaa 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_dropdown.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_dropdown.tsx @@ -51,6 +51,7 @@ export const AlertDropdown = () => { return ( <> diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criteria.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criteria.tsx index 3c474ee1d0ec6..555ac905d2963 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criteria.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criteria.tsx @@ -11,12 +11,11 @@ import { i18n } from '@kbn/i18n'; import { IFieldType } from 'src/plugins/data/public'; import { Criterion } from './criterion'; import { - AlertParams, - Comparator, - Criteria as CriteriaType, - Criterion as CriterionType, - CountCriteria as CountCriteriaType, - RatioCriteria as RatioCriteriaType, + PartialAlertParams, + PartialCountCriteria as PartialCountCriteriaType, + PartialCriteria as PartialCriteriaType, + PartialCriterion as PartialCriterionType, + PartialRatioCriteria as PartialRatioCriteriaType, isRatioAlert, getNumerator, getDenominator, @@ -25,8 +24,6 @@ import { Errors, CriterionErrors } from '../../validation'; import { ExpressionLike } from './editor'; import { CriterionPreview } from './criterion_preview_chart'; -const DEFAULT_CRITERIA = { field: 'log.level', comparator: Comparator.EQ, value: 'error' }; - const QueryAText = i18n.translate('xpack.infra.logs.alerting.threshold.ratioCriteriaQueryAText', { defaultMessage: 'Query A', }); @@ -37,11 +34,12 @@ const QueryBText = i18n.translate('xpack.infra.logs.alerting.threshold.ratioCrit interface SharedProps { fields: IFieldType[]; - criteria?: AlertParams['criteria']; + criteria?: PartialCriteriaType; + defaultCriterion: PartialCriterionType; errors: Errors['criteria']; - alertParams: Partial; + alertParams: PartialAlertParams; sourceId: string; - updateCriteria: (criteria: AlertParams['criteria']) => void; + updateCriteria: (criteria: PartialCriteriaType) => void; } type CriteriaProps = SharedProps; @@ -60,10 +58,10 @@ export const Criteria: React.FC = (props) => { interface CriteriaWrapperProps { alertParams: SharedProps['alertParams']; fields: SharedProps['fields']; - updateCriterion: (idx: number, params: Partial) => void; + updateCriterion: (idx: number, params: PartialCriterionType) => void; removeCriterion: (idx: number) => void; addCriterion: () => void; - criteria: CriteriaType; + criteria: PartialCountCriteriaType; errors: CriterionErrors; sourceId: SharedProps['sourceId']; isRatio?: boolean; @@ -118,29 +116,24 @@ const CriteriaWrapper: React.FC = (props) => { ); }; -interface RatioCriteriaProps { - alertParams: SharedProps['alertParams']; - fields: SharedProps['fields']; - criteria: RatioCriteriaType; - errors: Errors['criteria']; - sourceId: SharedProps['sourceId']; - updateCriteria: (criteria: AlertParams['criteria']) => void; +interface RatioCriteriaProps extends SharedProps { + criteria: PartialRatioCriteriaType; } const RatioCriteria: React.FC = (props) => { - const { criteria, errors, updateCriteria } = props; + const { criteria, defaultCriterion, errors, updateCriteria } = props; const handleUpdateNumeratorCriteria = useCallback( - (criteriaParam: CriteriaType) => { - const nextCriteria: RatioCriteriaType = [criteriaParam, getDenominator(criteria)]; + (criteriaParam: PartialCountCriteriaType) => { + const nextCriteria: PartialRatioCriteriaType = [criteriaParam, getDenominator(criteria)]; updateCriteria(nextCriteria); }, [updateCriteria, criteria] ); const handleUpdateDenominatorCriteria = useCallback( - (criteriaParam: CriteriaType) => { - const nextCriteria: RatioCriteriaType = [getNumerator(criteria), criteriaParam]; + (criteriaParam: PartialCountCriteriaType) => { + const nextCriteria: PartialRatioCriteriaType = [getNumerator(criteria), criteriaParam]; updateCriteria(nextCriteria); }, [updateCriteria, criteria] @@ -150,13 +143,13 @@ const RatioCriteria: React.FC = (props) => { updateCriterion: updateNumeratorCriterion, addCriterion: addNumeratorCriterion, removeCriterion: removeNumeratorCriterion, - } = useCriteriaState(getNumerator(criteria), handleUpdateNumeratorCriteria); + } = useCriteriaState(getNumerator(criteria), defaultCriterion, handleUpdateNumeratorCriteria); const { updateCriterion: updateDenominatorCriterion, addCriterion: addDenominatorCriterion, removeCriterion: removeDenominatorCriterion, - } = useCriteriaState(getDenominator(criteria), handleUpdateDenominatorCriteria); + } = useCriteriaState(getDenominator(criteria), defaultCriterion, handleUpdateDenominatorCriteria); return ( <> @@ -191,28 +184,17 @@ const RatioCriteria: React.FC = (props) => { ); }; -interface CountCriteriaProps { - alertParams: SharedProps['alertParams']; - fields: SharedProps['fields']; - criteria: CountCriteriaType; - errors: Errors['criteria']; - sourceId: SharedProps['sourceId']; - updateCriteria: (criteria: AlertParams['criteria']) => void; +interface CountCriteriaProps extends SharedProps { + criteria: PartialCountCriteriaType; } const CountCriteria: React.FC = (props) => { - const { criteria, updateCriteria, errors } = props; - - const handleUpdateCriteria = useCallback( - (criteriaParam: CriteriaType) => { - updateCriteria(criteriaParam); - }, - [updateCriteria] - ); + const { criteria, defaultCriterion, updateCriteria, errors } = props; const { updateCriterion, addCriterion, removeCriterion } = useCriteriaState( criteria, - handleUpdateCriteria + defaultCriterion, + updateCriteria ); return ( @@ -227,8 +209,9 @@ const CountCriteria: React.FC = (props) => { }; const useCriteriaState = ( - criteria: CriteriaType, - onUpdateCriteria: (criteria: CriteriaType) => void + criteria: PartialCountCriteriaType, + defaultCriterion: PartialCriterionType, + onUpdateCriteria: (criteria: PartialCountCriteriaType) => void ) => { const updateCriterion = useCallback( (idx, criterionParams) => { @@ -241,13 +224,13 @@ const useCriteriaState = ( ); const addCriterion = useCallback(() => { - const nextCriteria = criteria ? [...criteria, DEFAULT_CRITERIA] : [DEFAULT_CRITERIA]; + const nextCriteria = [...criteria, defaultCriterion]; onUpdateCriteria(nextCriteria); - }, [criteria, onUpdateCriteria]); + }, [criteria, defaultCriterion, onUpdateCriteria]); const removeCriterion = useCallback( (idx) => { - const nextCriteria = criteria.filter((criterion, index) => { + const nextCriteria = criteria.filter((_criterion, index) => { return index !== idx; }); onUpdateCriteria(nextCriteria); diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion.tsx index b2992ead3ea1b..9763a973d2fbd 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion.tsx @@ -90,7 +90,7 @@ const getFieldInfo = (fields: IFieldType[], fieldName: string): IFieldType | und interface Props { idx: number; fields: IFieldType[]; - criterion: CriterionType; + criterion: Partial; updateCriterion: (idx: number, params: Partial) => void; removeCriterion: (idx: number) => void; canDelete: boolean; @@ -116,7 +116,11 @@ export const Criterion: React.FC = ({ }, [fields]); const fieldInfo: IFieldType | undefined = useMemo(() => { - return getFieldInfo(fields, criterion.field); + if (criterion.field) { + return getFieldInfo(fields, criterion.field); + } else { + return undefined; + } }, [fields, criterion]); const compatibleComparatorOptions = useMemo(() => { @@ -129,10 +133,8 @@ export const Criterion: React.FC = ({ const nextFieldInfo = getFieldInfo(fields, fieldName); // If the field information we're dealing with has changed, reset the comparator and value. if ( - fieldInfo && - nextFieldInfo && - (fieldInfo.type !== nextFieldInfo.type || - fieldInfo.aggregatable !== nextFieldInfo.aggregatable) + fieldInfo?.type !== nextFieldInfo?.type || + fieldInfo?.aggregatable !== nextFieldInfo?.aggregatable ) { const compatibleComparators = getCompatibleComparatorsForField(nextFieldInfo); updateCriterion(idx, { @@ -160,7 +162,7 @@ export const Criterion: React.FC = ({ idx === 0 ? firstCriterionFieldPrefix : successiveCriterionFieldPrefix } uppercase={true} - value={criterion.field} + value={criterion.field ?? 'a chosen field'} isActive={isFieldPopoverOpen} color={errors.field.length === 0 ? 'secondary' : 'danger'} onClick={(e) => { @@ -180,7 +182,8 @@ export const Criterion: React.FC = ({ 0} error={errors.field}> @@ -194,9 +197,11 @@ export const Criterion: React.FC = ({ button={ = ({ 0} error={errors.comparator}> updateCriterion(idx, { comparator: e.target.value as Comparator }) diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx index 47dc419022880..cb759afa66d5c 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx @@ -34,7 +34,7 @@ import { NUM_BUCKETS, } from '../../../common/criterion_preview_chart/criterion_preview_chart'; import { - AlertParams, + PartialAlertParams, Threshold, Criterion, Comparator, @@ -50,7 +50,7 @@ import { decodeOrThrow } from '../../../../../common/runtime_types'; const GROUP_LIMIT = 5; interface Props { - alertParams: Partial; + alertParams: PartialAlertParams; chartCriterion: Partial; sourceId: string; showThreshold: boolean; diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx index 854363aacca5c..f69ca798c01b0 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx @@ -4,25 +4,36 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useCallback, useMemo, useState } from 'react'; +import { EuiButton, EuiCallOut, EuiLoadingSpinner, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { EuiLoadingSpinner, EuiSpacer, EuiButton, EuiCallOut } from '@elastic/eui'; +import React, { useCallback, useMemo, useState } from 'react'; import useMount from 'react-use/lib/useMount'; import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; -import { GroupByExpression } from '../../../common/group_by_expression/group_by_expression'; -import { ForLastExpression } from '../../../../../../triggers_actions_ui/public'; import { - AlertParams, + AlertTypeParamsExpressionProps, + ForLastExpression, +} from '../../../../../../triggers_actions_ui/public'; +import { + PartialAlertParams, Comparator, - ThresholdType, isRatioAlert, + PartialCriteria as PartialCriteriaType, + ThresholdType, + timeUnitRT, } from '../../../../../common/alerting/logs/log_threshold/types'; -import { Threshold } from './threshold'; +import { decodeOrThrow } from '../../../../../common/runtime_types'; +import { ObjectEntries } from '../../../../../common/utility_types'; +import { + LogIndexField, + LogSourceProvider, + useLogSourceContext, +} from '../../../../containers/logs/log_source'; +import { useSourceId } from '../../../../containers/source_id'; +import { GroupByExpression } from '../../../common/group_by_expression/group_by_expression'; +import { errorsRT } from '../../validation'; import { Criteria } from './criteria'; +import { Threshold } from './threshold'; import { TypeSwitcher } from './type_switcher'; -import { useSourceId } from '../../../../containers/source_id'; -import { LogSourceProvider, useLogSourceContext } from '../../../../containers/logs/log_source'; -import { Errors } from '../../validation'; export interface ExpressionCriteria { field?: string; @@ -34,45 +45,46 @@ interface LogsContextMeta { isInternal?: boolean; } -interface Props { - errors: Errors; - alertParams: Partial; - setAlertParams(key: string, value: any): void; - setAlertProperty(key: string, value: any): void; - sourceId: string; - metadata: LogsContextMeta; -} - -const DEFAULT_CRITERIA = { field: 'log.level', comparator: Comparator.EQ, value: 'error' }; - const DEFAULT_BASE_EXPRESSION = { timeSize: 5, timeUnit: 'm', }; -const DEFAULT_COUNT_EXPRESSION = { +const DEFAULT_FIELD = 'log.level'; + +const createDefaultCriterion = ( + availableFields: LogIndexField[], + value: ExpressionCriteria['value'] +) => + availableFields.some((availableField) => availableField.name === DEFAULT_FIELD) + ? { field: DEFAULT_FIELD, comparator: Comparator.EQ, value } + : { field: undefined, comparator: undefined, value: undefined }; + +const createDefaultCountAlertParams = (availableFields: LogIndexField[]) => ({ ...DEFAULT_BASE_EXPRESSION, count: { value: 75, comparator: Comparator.GT, }, - criteria: [DEFAULT_CRITERIA], -}; + criteria: [createDefaultCriterion(availableFields, 'error')], +}); -const DEFAULT_RATIO_EXPRESSION = { +const createDefaultRatioAlertParams = (availableFields: LogIndexField[]) => ({ ...DEFAULT_BASE_EXPRESSION, count: { value: 2, comparator: Comparator.GT, }, criteria: [ - [DEFAULT_CRITERIA], - [{ field: 'log.level', comparator: Comparator.EQ, value: 'warning' }], + createDefaultCriterion(availableFields, 'error'), + createDefaultCriterion([], 'warning'), ], -}; +}); -export const ExpressionEditor: React.FC = (props) => { - const isInternal = props.metadata?.isInternal; +export const ExpressionEditor: React.FC< + AlertTypeParamsExpressionProps +> = (props) => { + const isInternal = props.metadata?.isInternal ?? false; const [sourceId] = useSourceId(); const { http } = useKibana().services; @@ -80,12 +92,12 @@ export const ExpressionEditor: React.FC = (props) => { <> {isInternal ? ( - + ) : ( - + )} @@ -93,7 +105,7 @@ export const ExpressionEditor: React.FC = (props) => { ); }; -export const SourceStatusWrapper: React.FC = (props) => { +export const SourceStatusWrapper: React.FC = ({ children }) => { const { initialize, isLoadingSourceStatus, @@ -101,7 +113,6 @@ export const SourceStatusWrapper: React.FC = (props) => { hasFailedLoadingSourceStatus, loadSourceStatus, } = useLogSourceContext(); - const { children } = props; useMount(() => { initialize(); @@ -136,16 +147,19 @@ export const SourceStatusWrapper: React.FC = (props) => { ); }; -export const Editor: React.FC = (props) => { - const { setAlertParams, alertParams, errors, sourceId } = props; +export const Editor: React.FC< + AlertTypeParamsExpressionProps +> = (props) => { + const { setAlertParams, alertParams, errors } = props; const [hasSetDefaults, setHasSetDefaults] = useState(false); - const { sourceStatus } = useLogSourceContext(); - useMount(() => { - for (const [key, value] of Object.entries({ ...DEFAULT_COUNT_EXPRESSION, ...alertParams })) { - setAlertParams(key, value); - } - setHasSetDefaults(true); - }); + const { sourceId, sourceStatus } = useLogSourceContext(); + + const { + criteria: criteriaErrors, + threshold: thresholdErrors, + timeSizeUnit: timeSizeUnitErrors, + timeWindowSize: timeWindowSizeErrors, + } = useMemo(() => decodeOrThrow(errorsRT)(errors), [errors]); const supportedFields = useMemo(() => { if (sourceStatus?.logIndexFields) { @@ -176,7 +190,7 @@ export const Editor: React.FC = (props) => { ); const updateCriteria = useCallback( - (criteria: AlertParams['criteria']) => { + (criteria: PartialCriteriaType) => { setAlertParams('criteria', criteria); }, [setAlertParams] @@ -191,7 +205,9 @@ export const Editor: React.FC = (props) => { const updateTimeUnit = useCallback( (tu: string) => { - setAlertParams('timeUnit', tu); + if (timeUnitRT.is(tu)) { + setAlertParams('timeUnit', tu); + } }, [setAlertParams] ); @@ -203,20 +219,31 @@ export const Editor: React.FC = (props) => { [setAlertParams] ); + const defaultCountAlertParams = useMemo(() => createDefaultCountAlertParams(supportedFields), [ + supportedFields, + ]); + const updateType = useCallback( (type: ThresholdType) => { - const defaults = type === 'count' ? DEFAULT_COUNT_EXPRESSION : DEFAULT_RATIO_EXPRESSION; + const defaults = + type === 'count' ? defaultCountAlertParams : createDefaultRatioAlertParams(supportedFields); // Reset properties that don't make sense switching from one context to the other - for (const [key, value] of Object.entries({ - criteria: defaults.criteria, - count: defaults.count, - })) { - setAlertParams(key, value); - } + setAlertParams('count', defaults.count); + setAlertParams('criteria', defaults.criteria); }, - [setAlertParams] + [defaultCountAlertParams, setAlertParams, supportedFields] ); + useMount(() => { + const newAlertParams = { ...defaultCountAlertParams, ...alertParams }; + for (const [key, value] of Object.entries(newAlertParams) as ObjectEntries< + typeof newAlertParams + >) { + setAlertParams(key, value); + } + setHasSetDefaults(true); + }); + // Wait until the alert param defaults have been set if (!hasSetDefaults) return null; @@ -224,7 +251,8 @@ export const Editor: React.FC = (props) => { = (props) => { comparator={alertParams.count?.comparator} value={alertParams.count?.value} updateThreshold={updateThreshold} - errors={errors.threshold} + errors={thresholdErrors} /> = (props) => { timeWindowUnit={alertParams.timeUnit} onChangeWindowSize={updateTimeSize} onChangeWindowUnit={updateTimeUnit} - errors={{ timeWindowSize: errors.timeWindowSize, timeSizeUnit: errors.timeSizeUnit }} + errors={{ timeWindowSize: timeWindowSizeErrors, timeSizeUnit: timeSizeUnitErrors }} /> void; } -const getThresholdType = (criteria: AlertParams['criteria']): ThresholdType => { +const getThresholdType = (criteria: PartialCriteria): ThresholdType => { return isRatioAlert(criteria) ? 'ratio' : 'count'; }; diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/log_threshold_alert_type.ts b/x-pack/plugins/infra/public/alerting/log_threshold/log_threshold_alert_type.ts index 7154a77496b81..6cdb81155ec9a 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/log_threshold_alert_type.ts +++ b/x-pack/plugins/infra/public/alerting/log_threshold/log_threshold_alert_type.ts @@ -6,10 +6,13 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import { AlertTypeModel } from '../../../../triggers_actions_ui/public'; -import { LOG_DOCUMENT_COUNT_ALERT_TYPE_ID } from '../../../common/alerting/logs/log_threshold/types'; +import { + LOG_DOCUMENT_COUNT_ALERT_TYPE_ID, + PartialAlertParams, +} from '../../../common/alerting/logs/log_threshold/types'; import { validateExpression } from './validation'; -export function getAlertType(): AlertTypeModel { +export function getAlertType(): AlertTypeModel { return { id: LOG_DOCUMENT_COUNT_ALERT_TYPE_ID, description: i18n.translate('xpack.infra.logs.alertFlyout.alertDescription', { diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/validation.ts b/x-pack/plugins/infra/public/alerting/log_threshold/validation.ts index 6630b3d079141..24d373558008d 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/validation.ts +++ b/x-pack/plugins/infra/public/alerting/log_threshold/validation.ts @@ -5,45 +5,53 @@ */ import { i18n } from '@kbn/i18n'; +import * as rt from 'io-ts'; import { isNumber, isFinite } from 'lodash'; -import { ValidationResult } from '../../../../triggers_actions_ui/public'; +import { IErrorObject, ValidationResult } from '../../../../triggers_actions_ui/public'; import { - AlertParams, - Criteria, - RatioCriteria, + PartialCountCriteria, isRatioAlert, getNumerator, getDenominator, + PartialRequiredAlertParams, + PartialCriteria, } from '../../../common/alerting/logs/log_threshold/types'; -export interface CriterionErrors { - [id: string]: { - field: string[]; - comparator: string[]; - value: string[]; - }; -} +export const criterionErrorRT = rt.type({ + field: rt.array(rt.string), + comparator: rt.array(rt.string), + value: rt.array(rt.string), +}); -export interface Errors { - threshold: { - value: string[]; - }; +export const criterionErrorsRT = rt.record(rt.string, criterionErrorRT); + +export type CriterionErrors = rt.TypeOf; + +const alertingErrorRT: rt.Type = rt.recursion('AlertingError', () => + rt.record(rt.string, rt.union([rt.string, rt.array(rt.string), alertingErrorRT])) +); + +export const errorsRT = rt.type({ + threshold: rt.type({ + value: rt.array(rt.string), + }), // NOTE: The data structure for criteria errors isn't 100% // ideal but we need to conform to the interfaces that the alerting // framework expects. - criteria: { - [id: string]: CriterionErrors; - }; - timeWindowSize: string[]; - timeSizeUnit: string[]; -} + criteria: rt.record(rt.string, criterionErrorsRT), + timeWindowSize: rt.array(rt.string), + timeSizeUnit: rt.array(rt.string), +}); + +export type Errors = rt.TypeOf; export function validateExpression({ count, criteria, timeSize, - timeUnit, -}: Partial): ValidationResult { +}: PartialRequiredAlertParams & { + criteria: PartialCriteria; +}): ValidationResult { const validationResult = { errors: {} }; // NOTE: In the case of components provided by the Alerting framework the error property names @@ -79,7 +87,7 @@ export function validateExpression({ // Criteria validation if (criteria && criteria.length > 0) { - const getCriterionErrors = (_criteria: Criteria): CriterionErrors => { + const getCriterionErrors = (_criteria: PartialCountCriteria): CriterionErrors => { const _errors: CriterionErrors = {}; _criteria.forEach((criterion, idx) => { @@ -114,12 +122,12 @@ export function validateExpression({ }; if (!isRatioAlert(criteria)) { - const criteriaErrors = getCriterionErrors(criteria as Criteria); + const criteriaErrors = getCriterionErrors(criteria); errors.criteria[0] = criteriaErrors; } else { - const numeratorErrors = getCriterionErrors(getNumerator(criteria as RatioCriteria)); + const numeratorErrors = getCriterionErrors(getNumerator(criteria)); errors.criteria[0] = numeratorErrors; - const denominatorErrors = getCriterionErrors(getDenominator(criteria as RatioCriteria)); + const denominatorErrors = getCriterionErrors(getDenominator(criteria)); errors.criteria[1] = denominatorErrors; } } diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/index.ts b/x-pack/plugins/infra/public/alerting/metric_threshold/index.ts index cccd5fbc439d7..9c32c473f4597 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/index.ts +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/index.ts @@ -8,10 +8,18 @@ import React from 'react'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types'; import { validateMetricThreshold } from './components/validation'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { METRIC_THRESHOLD_ALERT_TYPE_ID } from '../../../server/lib/alerting/metric_threshold/types'; +import { AlertTypeParams } from '../../../../alerts/common'; +import { + MetricExpressionParams, + METRIC_THRESHOLD_ALERT_TYPE_ID, + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../server/lib/alerting/metric_threshold/types'; + +interface MetricThresholdAlertTypeParams extends AlertTypeParams { + criteria: MetricExpressionParams[]; +} -export function createMetricThresholdAlertType(): AlertTypeModel { +export function createMetricThresholdAlertType(): AlertTypeModel { return { id: METRIC_THRESHOLD_ALERT_TYPE_ID, description: i18n.translate('xpack.infra.metrics.alertFlyout.alertDescription', { diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_context_menu.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_context_menu.tsx index fe57b9db0e8b7..8582be008a44a 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_context_menu.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_context_menu.tsx @@ -79,7 +79,13 @@ export const LogEntryContextMenu: React.FC = ({ return ( - + diff --git a/x-pack/plugins/infra/public/containers/logs/log_source/log_source.ts b/x-pack/plugins/infra/public/containers/logs/log_source/log_source.ts index 879d2d95d7946..d7f40f603a9f7 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_source/log_source.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_source/log_source.ts @@ -9,6 +9,7 @@ import { useCallback, useMemo, useState } from 'react'; import useMountedState from 'react-use/lib/useMountedState'; import type { HttpHandler } from 'src/core/public'; import { + LogIndexField, LogSourceConfiguration, LogSourceConfigurationProperties, LogSourceConfigurationPropertiesPatch, @@ -20,6 +21,7 @@ import { callFetchLogSourceStatusAPI } from './api/fetch_log_source_status'; import { callPatchLogSourceConfigurationAPI } from './api/patch_log_source_configuration'; export { + LogIndexField, LogSourceConfiguration, LogSourceConfigurationProperties, LogSourceConfigurationPropertiesPatch, diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx index 98367335d9c2d..6fc9ce3d8983e 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx @@ -178,21 +178,19 @@ export const LogEntryCategoriesResultsContent: React.FunctionComponent - - - - - - - - + + + + + + { )} size="s" color="primary" - iconType="plusInCircle" + iconType="indexOpen" > {ADD_DATA_LABEL} diff --git a/x-pack/plugins/infra/public/pages/logs/settings/log_columns_configuration_panel.tsx b/x-pack/plugins/infra/public/pages/logs/settings/log_columns_configuration_panel.tsx index 3f109e7383c0e..62f2d89b65fdf 100644 --- a/x-pack/plugins/infra/public/pages/logs/settings/log_columns_configuration_panel.tsx +++ b/x-pack/plugins/infra/public/pages/logs/settings/log_columns_configuration_panel.tsx @@ -59,7 +59,7 @@ export const LogColumnsConfigurationPanel: React.FunctionComponent

    @@ -182,7 +182,7 @@ const FieldLogColumnConfigurationPanel: React.FunctionComponent<{ ); return ( - +
    @@ -212,7 +212,7 @@ const ExplainedLogColumnConfigurationPanel: React.FunctionComponent<{ - +
    diff --git a/x-pack/plugins/infra/public/pages/metrics/index.tsx b/x-pack/plugins/infra/public/pages/metrics/index.tsx index 222278dde3314..24f9598484d71 100644 --- a/x-pack/plugins/infra/public/pages/metrics/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/index.tsx @@ -92,7 +92,7 @@ export const InfrastructurePage = ({ match }: RouteComponentProps) => { )} size="s" color="primary" - iconType="plusInCircle" + iconType="indexOpen" > {ADD_DATA_LABEL} diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/process_row.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/process_row.tsx index 4718ed09dc9b2..3f0798c4a1670 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/process_row.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/process_row.tsx @@ -51,7 +51,7 @@ export const ProcessRow = ({ cells, item }: Props) => { {({ measureRef, bounds: { height = 0 } }) => ( - +
    @@ -81,7 +81,7 @@ export const ProcessRow = ({ cells, item }: Props) => { )} - + {i18n.translate( @@ -92,7 +92,7 @@ export const ProcessRow = ({ cells, item }: Props) => { )} - {item.pid} + {item.pid} @@ -105,12 +105,12 @@ export const ProcessRow = ({ cells, item }: Props) => { )} - {item.user} + {item.user} - + )} @@ -120,11 +120,15 @@ export const ProcessRow = ({ cells, item }: Props) => { ); }; -export const CodeLine = euiStyled(EuiCode).attrs({ +const ExpandedRowDescriptionList = euiStyled(EuiDescriptionList).attrs({ + compressed: true, +})` + width: 100%; +`; + +const CodeListItem = euiStyled(EuiCode).attrs({ transparentBackground: true, })` - text-overflow: ellipsis; - overflow: hidden; padding: 0 !important; & code.euiCodeBlock__code { white-space: nowrap !important; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/process_row_charts.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/process_row_charts.tsx index 7b7a285b5d6b8..af515ae75854c 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/process_row_charts.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/process_row_charts.tsx @@ -138,7 +138,7 @@ const ProcessChart = ({ timeseries, color, label }: ProcessChartProps) => { }; const ChartContainer = euiStyled.div` - width: 300px; + width: 100%; height: 140px; `; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/processes_table.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/processes_table.tsx index 3e4b066afa157..1ea6e397e7768 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/processes_table.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/processes_table.tsx @@ -28,7 +28,7 @@ import { FORMATTERS } from '../../../../../../../../common/formatters'; import { euiStyled } from '../../../../../../../../../observability/public'; import { SortBy } from '../../../../hooks/use_process_list'; import { Process } from './types'; -import { ProcessRow, CodeLine } from './process_row'; +import { ProcessRow } from './process_row'; import { StateBadge } from './state_badge'; import { STATE_ORDER } from './states'; @@ -150,7 +150,7 @@ export const ProcessesTable = ({ return ( <> - + {columns.map((column) => ( @@ -296,3 +296,11 @@ const columns: Array<{ render: (value: number) => FORMATTERS.percent(value), }, ]; + +const CodeLine = euiStyled.div` + font-family: ${(props) => props.theme.eui.euiCodeFontFamily}; + font-size: ${(props) => props.theme.eui.euiFontSizeS}; + white-space: pre; + overflow: hidden; + text-overflow: ellipsis; +`; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/summary_table.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/summary_table.tsx index 6efabf1b8c0ae..5bbba906b62f2 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/summary_table.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/processes/summary_table.tsx @@ -7,7 +7,15 @@ import React, { useMemo } from 'react'; import { mapValues } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { EuiBasicTable, EuiLoadingSpinner, EuiBasicTableColumn } from '@elastic/eui'; +import { + EuiLoadingSpinner, + EuiFlexGroup, + EuiFlexItem, + EuiDescriptionList, + EuiDescriptionListTitle, + EuiDescriptionListDescription, + EuiHorizontalRule, +} from '@elastic/eui'; import { euiStyled } from '../../../../../../../../../observability/public'; import { ProcessListAPIResponse } from '../../../../../../../../common/http_api'; import { STATE_NAMES } from './states'; @@ -17,63 +25,51 @@ interface Props { isLoading: boolean; } -type SummaryColumn = { +type SummaryRecord = { total: number; } & Record; export const SummaryTable = ({ processSummary, isLoading }: Props) => { const processCount = useMemo( () => - [ - { - total: isLoading ? -1 : processSummary.total, - ...mapValues(STATE_NAMES, () => (isLoading ? -1 : 0)), - ...(isLoading ? {} : processSummary), - }, - ] as SummaryColumn[], + ({ + total: isLoading ? -1 : processSummary.total, + ...mapValues(STATE_NAMES, () => (isLoading ? -1 : 0)), + ...(isLoading ? {} : processSummary), + } as SummaryRecord), [processSummary, isLoading] ); return ( - - - + <> + + {Object.entries(processCount).map(([field, value]) => ( + + + {columnTitles[field as keyof SummaryRecord]} + + {value === -1 ? : value} + + + + ))} + + + ); }; -const loadingRenderer = (value: number) => (value === -1 ? : value); - -const columns = [ - { - field: 'total', - name: i18n.translate('xpack.infra.metrics.nodeDetails.processes.headingTotalProcesses', { - defaultMessage: 'Total processes', - }), - width: 125, - render: loadingRenderer, - }, - ...Object.entries(STATE_NAMES).map(([field, name]) => ({ field, name, render: loadingRenderer })), -] as Array>; +const columnTitles = { + total: i18n.translate('xpack.infra.metrics.nodeDetails.processes.headingTotalProcesses', { + defaultMessage: 'Total processes', + }), + ...STATE_NAMES, +}; const LoadingSpinner = euiStyled(EuiLoadingSpinner).attrs({ size: 'm' })` margin-top: 2px; margin-bottom: 3px; `; -const StyleWrapper = euiStyled.div` - & .euiTableHeaderCell { - border-bottom: none; - & .euiTableCellContent { - padding-bottom: 0; - } - & .euiTableCellContent__text { - font-size: ${(props) => props.theme.eui.euiFontSizeS}; - } - } - - & .euiTableRowCell { - border-top: none; - & .euiTableCellContent { - padding-top: 0; - } - } +const ColumnTitle = euiStyled(EuiDescriptionListTitle)` + white-space: nowrap; `; diff --git a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts index 1941ec6326ddb..54cf8658a3f0d 100644 --- a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts @@ -9,7 +9,11 @@ import moment from 'moment'; import { getCustomMetricLabel } from '../../../../common/formatters/get_custom_metric_label'; import { toMetricOpt } from '../../../../common/snapshot_metric_i18n'; import { AlertStates, InventoryMetricConditions } from './types'; -import { RecoveredActionGroup } from '../../../../../alerts/common'; +import { + AlertInstanceContext, + AlertInstanceState, + RecoveredActionGroup, +} from '../../../../../alerts/common'; import { AlertExecutorOptions } from '../../../../../alerts/server'; import { InventoryItemType, SnapshotMetricType } from '../../../../common/inventory_models/types'; import { InfraBackendLibs } from '../../infra_types'; @@ -35,7 +39,15 @@ interface InventoryMetricThresholdParams { export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) => async ({ services, params, -}: AlertExecutorOptions) => { +}: AlertExecutorOptions< + /** + * TODO: Remove this use of `any` by utilizing a proper type + */ + Record, + Record, + AlertInstanceState, + AlertInstanceContext +>) => { const { criteria, filterQuery, diff --git a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_alert_type.ts b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_alert_type.ts index 2d1df6e8cb462..a2e8eff34ef98 100644 --- a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_alert_type.ts +++ b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_alert_type.ts @@ -5,7 +5,7 @@ */ import { schema } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; -import { AlertType } from '../../../../../alerts/server'; +import { AlertType, AlertInstanceState, AlertInstanceContext } from '../../../../../alerts/server'; import { createInventoryMetricThresholdExecutor, FIRED_ACTIONS, @@ -40,7 +40,17 @@ const condition = schema.object({ ), }); -export const registerMetricInventoryThresholdAlertType = (libs: InfraBackendLibs): AlertType => ({ +export const registerMetricInventoryThresholdAlertType = ( + libs: InfraBackendLibs +): AlertType< + /** + * TODO: Remove this use of `any` by utilizing a proper type + */ + Record, + Record, + AlertInstanceState, + AlertInstanceContext +> => ({ id: METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID, name: i18n.translate('xpack.infra.metrics.inventory.alertName', { defaultMessage: 'Inventory', diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts index d3d34cd2aad58..09d7e482772c2 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts @@ -9,7 +9,10 @@ import { AlertExecutorOptions, AlertServices, AlertInstance, + AlertTypeParams, + AlertTypeState, AlertInstanceContext, + AlertInstanceState, } from '../../../../../alerts/server'; import { AlertStates, @@ -20,12 +23,12 @@ import { UngroupedSearchQueryResponseRT, UngroupedSearchQueryResponse, GroupedSearchQueryResponse, - AlertParamsRT, + alertParamsRT, isRatioAlertParams, hasGroupBy, getNumerator, getDenominator, - Criteria, + CountCriteria, CountAlertParams, RatioAlertParams, } from '../../../../common/alerting/logs/log_threshold/types'; @@ -34,6 +37,14 @@ import { getIntervalInSeconds } from '../../../utils/get_interval_in_seconds'; import { decodeOrThrow } from '../../../../common/runtime_types'; import { UNGROUPED_FACTORY_KEY } from '../common/utils'; +type LogThresholdAlertServices = AlertServices; +type LogThresholdAlertExecutorOptions = AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext +>; + const COMPOSITE_GROUP_SIZE = 40; const checkValueAgainstComparatorMap: { @@ -46,7 +57,7 @@ const checkValueAgainstComparatorMap: { }; export const createLogThresholdExecutor = (libs: InfraBackendLibs) => - async function ({ services, params }: AlertExecutorOptions) { + async function ({ services, params }: LogThresholdAlertExecutorOptions) { const { alertInstanceFactory, savedObjectsClient, callCluster } = services; const { sources } = libs; @@ -56,7 +67,7 @@ export const createLogThresholdExecutor = (libs: InfraBackendLibs) => const alertInstance = alertInstanceFactory(UNGROUPED_FACTORY_KEY); try { - const validatedParams = decodeOrThrow(AlertParamsRT)(params); + const validatedParams = decodeOrThrow(alertParamsRT)(params); if (!isRatioAlertParams(validatedParams)) { await executeAlert( @@ -88,8 +99,8 @@ async function executeAlert( alertParams: CountAlertParams, timestampField: string, indexPattern: string, - callCluster: AlertServices['callCluster'], - alertInstanceFactory: AlertServices['alertInstanceFactory'] + callCluster: LogThresholdAlertServices['callCluster'], + alertInstanceFactory: LogThresholdAlertServices['alertInstanceFactory'] ) { const query = getESQuery(alertParams, timestampField, indexPattern); @@ -118,8 +129,8 @@ async function executeRatioAlert( alertParams: RatioAlertParams, timestampField: string, indexPattern: string, - callCluster: AlertServices['callCluster'], - alertInstanceFactory: AlertServices['alertInstanceFactory'] + callCluster: LogThresholdAlertServices['callCluster'], + alertInstanceFactory: LogThresholdAlertServices['alertInstanceFactory'] ) { // Ratio alert params are separated out into two standard sets of alert params const numeratorParams: AlertParams = { @@ -163,7 +174,7 @@ async function executeRatioAlert( } const getESQuery = ( - alertParams: Omit & { criteria: Criteria }, + alertParams: Omit & { criteria: CountCriteria }, timestampField: string, indexPattern: string ) => { @@ -175,7 +186,7 @@ const getESQuery = ( export const processUngroupedResults = ( results: UngroupedSearchQueryResponse, params: CountAlertParams, - alertInstanceFactory: AlertExecutorOptions['services']['alertInstanceFactory'], + alertInstanceFactory: LogThresholdAlertExecutorOptions['services']['alertInstanceFactory'], alertInstaceUpdater: AlertInstanceUpdater ) => { const { count, criteria } = params; @@ -204,7 +215,7 @@ export const processUngroupedRatioResults = ( numeratorResults: UngroupedSearchQueryResponse, denominatorResults: UngroupedSearchQueryResponse, params: RatioAlertParams, - alertInstanceFactory: AlertExecutorOptions['services']['alertInstanceFactory'], + alertInstanceFactory: LogThresholdAlertExecutorOptions['services']['alertInstanceFactory'], alertInstaceUpdater: AlertInstanceUpdater ) => { const { count, criteria } = params; @@ -259,7 +270,7 @@ const getReducedGroupByResults = ( export const processGroupByResults = ( results: GroupedSearchQueryResponse['aggregations']['groups']['buckets'], params: CountAlertParams, - alertInstanceFactory: AlertExecutorOptions['services']['alertInstanceFactory'], + alertInstanceFactory: LogThresholdAlertExecutorOptions['services']['alertInstanceFactory'], alertInstaceUpdater: AlertInstanceUpdater ) => { const { count, criteria } = params; @@ -292,7 +303,7 @@ export const processGroupByRatioResults = ( numeratorResults: GroupedSearchQueryResponse['aggregations']['groups']['buckets'], denominatorResults: GroupedSearchQueryResponse['aggregations']['groups']['buckets'], params: RatioAlertParams, - alertInstanceFactory: AlertExecutorOptions['services']['alertInstanceFactory'], + alertInstanceFactory: LogThresholdAlertExecutorOptions['services']['alertInstanceFactory'], alertInstaceUpdater: AlertInstanceUpdater ) => { const { count, criteria } = params; @@ -355,7 +366,7 @@ export const updateAlertInstance: AlertInstanceUpdater = (alertInstance, state, }; export const buildFiltersFromCriteria = ( - params: Pick & { criteria: Criteria }, + params: Pick & { criteria: CountCriteria }, timestampField: string ) => { const { timeSize, timeUnit, criteria } = params; @@ -406,7 +417,7 @@ export const buildFiltersFromCriteria = ( }; export const getGroupedESQuery = ( - params: Pick & { criteria: Criteria }, + params: Pick & { criteria: CountCriteria }, timestampField: string, index: string ): object | undefined => { @@ -464,7 +475,7 @@ export const getGroupedESQuery = ( }; export const getUngroupedESQuery = ( - params: Pick & { criteria: Criteria }, + params: Pick & { criteria: CountCriteria }, timestampField: string, index: string ): object => { @@ -498,7 +509,7 @@ type Filter = { [key in SupportedESQueryTypes]?: object; }; -const buildFiltersForCriteria = (criteria: Criteria) => { +const buildFiltersForCriteria = (criteria: CountCriteria) => { let filters: Filter[] = []; criteria.forEach((criterion) => { @@ -599,11 +610,17 @@ const getQueryMappingForComparator = (comparator: Comparator) => { return queryMappings[comparator]; }; -const getUngroupedResults = async (query: object, callCluster: AlertServices['callCluster']) => { +const getUngroupedResults = async ( + query: object, + callCluster: LogThresholdAlertServices['callCluster'] +) => { return decodeOrThrow(UngroupedSearchQueryResponseRT)(await callCluster('search', query)); }; -const getGroupedResults = async (query: object, callCluster: AlertServices['callCluster']) => { +const getGroupedResults = async ( + query: object, + callCluster: LogThresholdAlertServices['callCluster'] +) => { let compositeGroupBuckets: GroupedSearchQueryResponse['aggregations']['groups']['buckets'] = []; let lastAfterKey: GroupedSearchQueryResponse['aggregations']['groups']['after_key'] | undefined; @@ -626,7 +643,7 @@ const getGroupedResults = async (query: object, callCluster: AlertServices['call return compositeGroupBuckets; }; -const createConditionsMessageForCriteria = (criteria: Criteria) => { +const createConditionsMessageForCriteria = (criteria: CountCriteria) => { const parts = criteria.map((criterion, index) => { const { field, comparator, value } = criterion; return `${index === 0 ? '' : 'and'} ${field} ${comparator} ${value}`; diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_alert_type.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_alert_type.ts index 4703371f5e0de..e248d3b3ddcfa 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_alert_type.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_alert_type.ts @@ -8,7 +8,7 @@ import { PluginSetupContract } from '../../../../../alerts/server'; import { createLogThresholdExecutor, FIRED_ACTIONS } from './log_threshold_executor'; import { LOG_DOCUMENT_COUNT_ALERT_TYPE_ID, - AlertParamsRT, + alertParamsRT, } from '../../../../common/alerting/logs/log_threshold/types'; import { InfraBackendLibs } from '../../infra_types'; import { decodeOrThrow } from '../../../../common/runtime_types'; @@ -86,7 +86,7 @@ export async function registerLogThresholdAlertType( }), validate: { params: { - validate: (params) => decodeOrThrow(AlertParamsRT)(params), + validate: (params) => decodeOrThrow(alertParamsRT)(params), }, }, defaultActionGroupId: FIRED_ACTIONS.id, diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/evaluate_alert.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/evaluate_alert.ts index 49f82c7ccec0b..d51d9435fc904 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/evaluate_alert.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/evaluate_alert.ts @@ -12,7 +12,7 @@ import { import { InfraSource } from '../../../../../common/http_api/source_api'; import { InfraDatabaseSearchResponse } from '../../../adapters/framework/adapter_types'; import { createAfterKeyHandler } from '../../../../utils/create_afterkey_handler'; -import { AlertServices, AlertExecutorOptions } from '../../../../../../alerts/server'; +import { AlertServices } from '../../../../../../alerts/server'; import { getAllCompositeData } from '../../../../utils/get_all_composite_data'; import { DOCUMENT_COUNT_I18N } from '../../common/messages'; import { UNGROUPED_FACTORY_KEY } from '../../common/utils'; @@ -35,17 +35,19 @@ interface CompositeAggregationsResponse { }; } -export const evaluateAlert = ( +export interface EvaluatedAlertParams { + criteria: MetricExpressionParams[]; + groupBy: string | undefined | string[]; + filterQuery: string | undefined; +} + +export const evaluateAlert = ( callCluster: AlertServices['callCluster'], - params: AlertExecutorOptions['params'], + params: Params, config: InfraSource['configuration'], timeframe?: { start: number; end: number } ) => { - const { criteria, groupBy, filterQuery } = params as { - criteria: MetricExpressionParams[]; - groupBy: string | undefined | string[]; - filterQuery: string | undefined; - }; + const { criteria, groupBy, filterQuery } = params; return Promise.all( criteria.map(async (criterion) => { const currentValues = await getMetric( diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts index a1d6428f3b52b..6c9fac9d1133c 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts @@ -7,13 +7,13 @@ import { createMetricThresholdExecutor, FIRED_ACTIONS } from './metric_threshold import { Comparator, AlertStates } from './types'; import * as mocks from './test_mocks'; import { RecoveredActionGroup } from '../../../../../alerts/common'; -import { AlertExecutorOptions } from '../../../../../alerts/server'; import { alertsMock, AlertServicesMock, AlertInstanceMock, } from '../../../../../alerts/server/mocks'; import { InfraSources } from '../../sources'; +import { MetricThresholdAlertExecutorOptions } from './register_metric_threshold_alert_type'; interface AlertTestInstance { instance: AlertInstanceMock; @@ -23,11 +23,23 @@ interface AlertTestInstance { let persistAlertInstances = false; +const mockOptions = { + alertId: '', + startedAt: new Date(), + previousStartedAt: null, + state: {}, + spaceId: '', + name: '', + tags: [], + createdBy: null, + updatedBy: null, +}; + describe('The metric threshold alert type', () => { describe('querying the entire infrastructure', () => { const instanceID = '*'; const execute = (comparator: Comparator, threshold: number[], sourceId: string = 'default') => - executor({ + executor(({ services, params: { sourceId, @@ -39,7 +51,10 @@ describe('The metric threshold alert type', () => { }, ], }, - }); + /** + * TODO: Remove this use of `as` by utilizing a proper type + */ + } as unknown) as MetricThresholdAlertExecutorOptions); test('alerts as expected with the > comparator', async () => { await execute(Comparator.GT, [0.75]); expect(mostRecentAction(instanceID).id).toBe(FIRED_ACTIONS.id); @@ -109,6 +124,7 @@ describe('The metric threshold alert type', () => { describe('querying with a groupBy parameter', () => { const execute = (comparator: Comparator, threshold: number[]) => executor({ + ...mockOptions, services, params: { groupBy: 'something', @@ -159,6 +175,7 @@ describe('The metric threshold alert type', () => { groupBy: string = '' ) => executor({ + ...mockOptions, services, params: { groupBy, @@ -216,6 +233,7 @@ describe('The metric threshold alert type', () => { const instanceID = '*'; const execute = (comparator: Comparator, threshold: number[]) => executor({ + ...mockOptions, services, params: { criteria: [ @@ -242,6 +260,7 @@ describe('The metric threshold alert type', () => { const instanceID = '*'; const execute = (comparator: Comparator, threshold: number[]) => executor({ + ...mockOptions, services, params: { criteria: [ @@ -268,6 +287,7 @@ describe('The metric threshold alert type', () => { const instanceID = '*'; const execute = (comparator: Comparator, threshold: number[]) => executor({ + ...mockOptions, services, params: { criteria: [ @@ -294,6 +314,7 @@ describe('The metric threshold alert type', () => { const instanceID = '*'; const execute = (alertOnNoData: boolean) => executor({ + ...mockOptions, services, params: { criteria: [ @@ -323,6 +344,7 @@ describe('The metric threshold alert type', () => { const instanceID = '*'; const execute = () => executor({ + ...mockOptions, services, params: { criteria: [ @@ -348,6 +370,7 @@ describe('The metric threshold alert type', () => { const instanceID = '*'; const execute = (threshold: number[]) => executor({ + ...mockOptions, services, params: { criteria: [ @@ -392,6 +415,7 @@ describe('The metric threshold alert type', () => { const instanceID = '*'; const execute = () => executor({ + ...mockOptions, services, params: { sourceId: 'default', @@ -435,10 +459,7 @@ const mockLibs: any = { configuration: createMockStaticConfiguration({}), }; -const executor = createMetricThresholdExecutor(mockLibs) as (opts: { - params: AlertExecutorOptions['params']; - services: { callCluster: AlertExecutorOptions['params']['callCluster'] }; -}) => Promise; +const executor = createMetricThresholdExecutor(mockLibs); const services: AlertServicesMock = alertsMock.createAlertServices(); services.callCluster.mockImplementation(async (_: string, { body, index }: any) => { diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts index 60790648d9a9b..d63b42cd3b146 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts @@ -7,7 +7,6 @@ import { first, last } from 'lodash'; import { i18n } from '@kbn/i18n'; import moment from 'moment'; import { RecoveredActionGroup } from '../../../../../alerts/common'; -import { AlertExecutorOptions } from '../../../../../alerts/server'; import { InfraBackendLibs } from '../../infra_types'; import { buildErrorAlertReason, @@ -18,10 +17,16 @@ import { } from '../common/messages'; import { createFormatter } from '../../../../common/formatters'; import { AlertStates } from './types'; -import { evaluateAlert } from './lib/evaluate_alert'; +import { evaluateAlert, EvaluatedAlertParams } from './lib/evaluate_alert'; +import { + MetricThresholdAlertExecutorOptions, + MetricThresholdAlertType, +} from './register_metric_threshold_alert_type'; -export const createMetricThresholdExecutor = (libs: InfraBackendLibs) => - async function (options: AlertExecutorOptions) { +export const createMetricThresholdExecutor = ( + libs: InfraBackendLibs +): MetricThresholdAlertType['executor'] => + async function (options: MetricThresholdAlertExecutorOptions) { const { services, params } = options; const { criteria } = params; if (criteria.length === 0) throw new Error('Cannot execute an alert with 0 conditions'); @@ -36,7 +41,11 @@ export const createMetricThresholdExecutor = (libs: InfraBackendLibs) => sourceId || 'default' ); const config = source.configuration; - const alertResults = await evaluateAlert(services.callCluster, params, config); + const alertResults = await evaluateAlert( + services.callCluster, + params as EvaluatedAlertParams, + config + ); // Because each alert result has the same group definitions, just grab the groups from the first one. const groups = Object.keys(first(alertResults)!); diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts index f04a1015bcbcd..000c89f5899ef 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts @@ -5,7 +5,12 @@ */ import { schema } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; -import { AlertType } from '../../../../../alerts/server'; +import { + AlertType, + AlertInstanceState, + AlertInstanceContext, + AlertExecutorOptions, +} from '../../../../../alerts/server'; import { METRIC_EXPLORER_AGGREGATIONS } from '../../../../common/http_api/metrics_explorer'; import { createMetricThresholdExecutor, FIRED_ACTIONS } from './metric_threshold_executor'; import { METRIC_THRESHOLD_ALERT_TYPE_ID, Comparator } from './types'; @@ -21,7 +26,26 @@ import { thresholdActionVariableDescription, } from '../common/messages'; -export function registerMetricThresholdAlertType(libs: InfraBackendLibs): AlertType { +export type MetricThresholdAlertType = AlertType< + /** + * TODO: Remove this use of `any` by utilizing a proper type + */ + Record, + Record, + AlertInstanceState, + AlertInstanceContext +>; +export type MetricThresholdAlertExecutorOptions = AlertExecutorOptions< + /** + * TODO: Remove this use of `any` by utilizing a proper type + */ + Record, + Record, + AlertInstanceState, + AlertInstanceContext +>; + +export function registerMetricThresholdAlertType(libs: InfraBackendLibs): MetricThresholdAlertType { const baseCriterion = { threshold: schema.arrayOf(schema.number()), comparator: oneOfLiterals(Object.values(Comparator)), diff --git a/x-pack/plugins/lens/public/app_plugin/app.test.tsx b/x-pack/plugins/lens/public/app_plugin/app.test.tsx index 1496b0c335322..5e38cb49114e9 100644 --- a/x-pack/plugins/lens/public/app_plugin/app.test.tsx +++ b/x-pack/plugins/lens/public/app_plugin/app.test.tsx @@ -74,6 +74,16 @@ function createMockFrame(): jest.Mocked { }; } +function createMockSearchService() { + let sessionIdCounter = 1; + return { + session: { + start: jest.fn(() => `sessionId-${sessionIdCounter++}`), + clear: jest.fn(), + }, + }; +} + function createMockFilterManager() { const unsubscribe = jest.fn(); @@ -118,16 +128,29 @@ function createMockQueryString() { function createMockTimefilter() { const unsubscribe = jest.fn(); + let timeFilter = { from: 'now-7d', to: 'now' }; + let subscriber: () => void; return { - getTime: jest.fn(() => ({ from: 'now-7d', to: 'now' })), - setTime: jest.fn(), + getTime: jest.fn(() => timeFilter), + setTime: jest.fn((newTimeFilter) => { + timeFilter = newTimeFilter; + if (subscriber) { + subscriber(); + } + }), getTimeUpdate$: () => ({ subscribe: ({ next }: { next: () => void }) => { + subscriber = next; return unsubscribe; }, }), getRefreshInterval: () => {}, getRefreshIntervalDefaults: () => {}, + getAutoRefreshFetch$: () => ({ + subscribe: ({ next }: { next: () => void }) => { + return next; + }, + }), }; } @@ -209,6 +232,7 @@ describe('Lens App', () => { return new Promise((resolve) => resolve({ id })); }), }, + search: createMockSearchService(), } as unknown) as DataPublicPluginStart, storage: { get: jest.fn(), @@ -295,6 +319,7 @@ describe('Lens App', () => { "query": "", }, "savedQuery": undefined, + "searchSessionId": "sessionId-1", "showNoDataPopover": [Function], }, ], @@ -1072,6 +1097,53 @@ describe('Lens App', () => { }) ); }); + + it('updates the searchSessionId when the user changes query or time in the search bar', () => { + const { component, frame, services } = mountWith({}); + act(() => + component.find(TopNavMenu).prop('onQuerySubmit')!({ + dateRange: { from: 'now-14d', to: 'now-7d' }, + query: { query: '', language: 'lucene' }, + }) + ); + component.update(); + expect(frame.mount).toHaveBeenCalledWith( + expect.any(Element), + expect.objectContaining({ + searchSessionId: `sessionId-1`, + }) + ); + + // trigger again, this time changing just the query + act(() => + component.find(TopNavMenu).prop('onQuerySubmit')!({ + dateRange: { from: 'now-14d', to: 'now-7d' }, + query: { query: 'new', language: 'lucene' }, + }) + ); + component.update(); + expect(frame.mount).toHaveBeenCalledWith( + expect.any(Element), + expect.objectContaining({ + searchSessionId: `sessionId-2`, + }) + ); + + const indexPattern = ({ id: 'index1' } as unknown) as IIndexPattern; + const field = ({ name: 'myfield' } as unknown) as IFieldType; + act(() => + services.data.query.filterManager.setFilters([ + esFilters.buildExistsFilter(field, indexPattern), + ]) + ); + component.update(); + expect(frame.mount).toHaveBeenCalledWith( + expect.any(Element), + expect.objectContaining({ + searchSessionId: `sessionId-3`, + }) + ); + }); }); describe('saved query handling', () => { @@ -1165,6 +1237,37 @@ describe('Lens App', () => { ); }); + it('updates the searchSessionId when the query is updated', () => { + const { component, frame } = mountWith({}); + act(() => { + component.find(TopNavMenu).prop('onSaved')!({ + id: '1', + attributes: { + title: '', + description: '', + query: { query: '', language: 'lucene' }, + }, + }); + }); + act(() => { + component.find(TopNavMenu).prop('onSavedQueryUpdated')!({ + id: '2', + attributes: { + title: 'new title', + description: '', + query: { query: '', language: 'lucene' }, + }, + }); + }); + component.update(); + expect(frame.mount).toHaveBeenCalledWith( + expect.any(Element), + expect.objectContaining({ + searchSessionId: `sessionId-1`, + }) + ); + }); + it('clears all existing unpinned filters when the active saved query is cleared', () => { const { component, frame, services } = mountWith({}); act(() => @@ -1190,6 +1293,32 @@ describe('Lens App', () => { }) ); }); + + it('updates the searchSessionId when the active saved query is cleared', () => { + const { component, frame, services } = mountWith({}); + act(() => + component.find(TopNavMenu).prop('onQuerySubmit')!({ + dateRange: { from: 'now-14d', to: 'now-7d' }, + query: { query: 'new', language: 'lucene' }, + }) + ); + const indexPattern = ({ id: 'index1' } as unknown) as IIndexPattern; + const field = ({ name: 'myfield' } as unknown) as IFieldType; + const pinnedField = ({ name: 'pinnedField' } as unknown) as IFieldType; + const unpinned = esFilters.buildExistsFilter(field, indexPattern); + const pinned = esFilters.buildExistsFilter(pinnedField, indexPattern); + FilterManager.setFiltersStore([pinned], esFilters.FilterStateStore.GLOBAL_STATE); + act(() => services.data.query.filterManager.setFilters([pinned, unpinned])); + component.update(); + act(() => component.find(TopNavMenu).prop('onClearSavedQuery')!()); + component.update(); + expect(frame.mount).toHaveBeenCalledWith( + expect.any(Element), + expect.objectContaining({ + searchSessionId: `sessionId-2`, + }) + ); + }); }); describe('showing a confirm message when leaving', () => { diff --git a/x-pack/plugins/lens/public/app_plugin/app.tsx b/x-pack/plugins/lens/public/app_plugin/app.tsx index bb77c5998519d..3f10cb341105c 100644 --- a/x-pack/plugins/lens/public/app_plugin/app.tsx +++ b/x-pack/plugins/lens/public/app_plugin/app.tsx @@ -7,7 +7,7 @@ import './app.scss'; import _ from 'lodash'; -import React, { useState, useEffect, useCallback } from 'react'; +import React, { useState, useEffect, useCallback, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { NotificationsStart } from 'kibana/public'; import { EuiBreadcrumb } from '@elastic/eui'; @@ -71,7 +71,6 @@ export function App({ } = useKibana().services; const [state, setState] = useState(() => { - const currentRange = data.query.timefilter.timefilter.getTime(); return { query: data.query.queryString.getQuery(), // Do not use app-specific filters from previous app, @@ -81,14 +80,11 @@ export function App({ : data.query.filterManager.getFilters(), isLoading: Boolean(initialInput), indexPatternsForTopNav: [], - dateRange: { - fromDate: currentRange.from, - toDate: currentRange.to, - }, isLinkedToOriginatingApp: Boolean(incomingState?.originatingApp), isSaveModalVisible: false, indicateNoData: false, isSaveable: false, + searchSessionId: data.search.session.start(), }; }); @@ -107,10 +103,14 @@ export function App({ state.indicateNoData, state.query, state.filters, - state.dateRange, state.indexPatternsForTopNav, + state.searchSessionId, ]); + // Need a stable reference for the frame component of the dateRange + const { from: fromDate, to: toDate } = data.query.timefilter.timefilter.getTime(); + const currentDateRange = useMemo(() => ({ fromDate, toDate }), [fromDate, toDate]); + const onError = useCallback( (e: { message: string }) => notifications.toasts.addDanger({ @@ -160,24 +160,35 @@ export function App({ const filterSubscription = data.query.filterManager.getUpdates$().subscribe({ next: () => { - setState((s) => ({ ...s, filters: data.query.filterManager.getFilters() })); + setState((s) => ({ + ...s, + filters: data.query.filterManager.getFilters(), + searchSessionId: data.search.session.start(), + })); trackUiEvent('app_filters_updated'); }, }); const timeSubscription = data.query.timefilter.timefilter.getTimeUpdate$().subscribe({ next: () => { - const currentRange = data.query.timefilter.timefilter.getTime(); setState((s) => ({ ...s, - dateRange: { - fromDate: currentRange.from, - toDate: currentRange.to, - }, + searchSessionId: data.search.session.start(), })); }, }); + const autoRefreshSubscription = data.query.timefilter.timefilter + .getAutoRefreshFetch$() + .subscribe({ + next: () => { + setState((s) => ({ + ...s, + searchSessionId: data.search.session.start(), + })); + }, + }); + const kbnUrlStateStorage = createKbnUrlStateStorage({ history, useHash: uiSettings.get('state:storeInSessionStorage'), @@ -192,10 +203,12 @@ export function App({ stopSyncingQueryServiceStateWithUrl(); filterSubscription.unsubscribe(); timeSubscription.unsubscribe(); + autoRefreshSubscription.unsubscribe(); }; }, [ data.query.filterManager, data.query.timefilter.timefilter, + data.search.session, notifications.toasts, uiSettings, data.query, @@ -594,21 +607,21 @@ export function App({ appName={'lens'} onQuerySubmit={(payload) => { const { dateRange, query } = payload; - if ( - dateRange.from !== state.dateRange.fromDate || - dateRange.to !== state.dateRange.toDate - ) { + const currentRange = data.query.timefilter.timefilter.getTime(); + if (dateRange.from !== currentRange.from || dateRange.to !== currentRange.to) { data.query.timefilter.timefilter.setTime(dateRange); trackUiEvent('app_date_change'); } else { + // Query has changed, renew the session id. + // Time change will be picked up by the time subscription + setState((s) => ({ + ...s, + searchSessionId: data.search.session.start(), + })); trackUiEvent('app_query_change'); } setState((s) => ({ ...s, - dateRange: { - fromDate: dateRange.from, - toDate: dateRange.to, - }, query: query || s.query, })); }} @@ -622,12 +635,6 @@ export function App({ setState((s) => ({ ...s, savedQuery: { ...savedQuery }, // Shallow query for reference issues - dateRange: savedQuery.attributes.timefilter - ? { - fromDate: savedQuery.attributes.timefilter.from, - toDate: savedQuery.attributes.timefilter.to, - } - : s.dateRange, })); }} onClearSavedQuery={() => { @@ -640,8 +647,8 @@ export function App({ })); }} query={state.query} - dateRangeFrom={state.dateRange.fromDate} - dateRangeTo={state.dateRange.toDate} + dateRangeFrom={fromDate} + dateRangeTo={toDate} indicateNoData={state.indicateNoData} />
    @@ -650,7 +657,8 @@ export function App({ className="lnsApp__frame" render={editorFrame.mount} nativeProps={{ - dateRange: state.dateRange, + searchSessionId: state.searchSessionId, + dateRange: currentDateRange, query: state.query, filters: state.filters, savedQuery: state.savedQuery, diff --git a/x-pack/plugins/lens/public/app_plugin/mounter.tsx b/x-pack/plugins/lens/public/app_plugin/mounter.tsx index fbfd9c5758948..e769e402ff0e1 100644 --- a/x-pack/plugins/lens/public/app_plugin/mounter.tsx +++ b/x-pack/plugins/lens/public/app_plugin/mounter.tsx @@ -216,6 +216,7 @@ export async function mountApp( params.element ); return () => { + data.search.session.clear(); instance.unmount(); unmountComponentAtNode(params.element); unlistenParentHistory(); diff --git a/x-pack/plugins/lens/public/app_plugin/types.ts b/x-pack/plugins/lens/public/app_plugin/types.ts index 869ccf52fb0bd..af0feabe68cf7 100644 --- a/x-pack/plugins/lens/public/app_plugin/types.ts +++ b/x-pack/plugins/lens/public/app_plugin/types.ts @@ -55,16 +55,12 @@ export interface LensAppState { // Determines whether the lens editor shows the 'save and return' button, and the originating app breadcrumb. isLinkedToOriginatingApp?: boolean; - // Properties needed to interface with TopNav - dateRange: { - fromDate: string; - toDate: string; - }; query: Query; filters: Filter[]; savedQuery?: SavedQuery; isSaveable: boolean; activeData?: TableInspectorAdapter; + searchSessionId: string; } export interface RedirectToOriginProps { diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx index b0879ac8cb886..ef95314c55581 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx @@ -60,6 +60,7 @@ function getDefaultProps() { }, palettes: chartPluginMock.createPaletteRegistry(), showNoDataPopover: jest.fn(), + searchSessionId: 'sessionId', }; } @@ -264,6 +265,7 @@ describe('editor_frame', () => { filters: [], dateRange: { fromDate: 'now-7d', toDate: 'now' }, availablePalettes: defaultProps.palettes, + searchSessionId: 'sessionId', }); }); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx index 977947b5afbeb..d872920d815ad 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx @@ -43,6 +43,7 @@ export interface EditorFrameProps { query: Query; filters: Filter[]; savedQuery?: SavedQuery; + searchSessionId: string; onChange: (arg: { filterableIndexPatterns: string[]; doc: Document; @@ -105,7 +106,7 @@ export function EditorFrame(props: EditorFrameProps) { dateRange: props.dateRange, query: props.query, filters: props.filters, - + searchSessionId: props.searchSessionId, availablePalettes: props.palettes, addNewLayer() { diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.test.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.test.ts index 792fdc6d1ace7..52328bc3a1440 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.test.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.test.ts @@ -39,6 +39,7 @@ describe('editor_frame state management', () => { query: { query: '', language: 'lucene' }, filters: [], showNoDataPopover: jest.fn(), + searchSessionId: 'sessionId', }; }); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx index 338a998b6b4dc..e2c4fa959924a 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx @@ -273,16 +273,18 @@ export function SuggestionPanel({ const contextRef = useRef(context); contextRef.current = context; + const sessionIdRef = useRef(frame.searchSessionId); + sessionIdRef.current = frame.searchSessionId; + const AutoRefreshExpressionRenderer = useMemo(() => { - const autoRefreshFetch$ = plugins.data.query.timefilter.timefilter.getAutoRefreshFetch$(); return (props: ReactExpressionRendererProps) => ( ); - }, [plugins.data.query.timefilter.timefilter, ExpressionRendererComponent]); + }, [ExpressionRendererComponent]); const [lastSelectedSuggestion, setLastSelectedSuggestion] = useState(-1); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx index 8820f26479cf9..eb16dabfd2f90 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx @@ -162,7 +162,7 @@ export function WorkspacePanel({ const expression = useMemo( () => { - if (!configurationValidationError || configurationValidationError.length === 0) { + if (!configurationValidationError?.length) { try { return buildExpression({ visualization: activeVisualization, @@ -362,8 +362,6 @@ export const InnerVisualizationWrapper = ({ }; ExpressionRendererComponent: ReactExpressionRendererType; }) => { - const autoRefreshFetch$ = useMemo(() => timefilter.getAutoRefreshFetch$(), [timefilter]); - const context: ExecutionContextSearch = useMemo( () => ({ query: framePublicAPI.query, @@ -400,13 +398,17 @@ export const InnerVisualizationWrapper = ({ showExtraErrors = localState.configurationValidationError .slice(1) .map(({ longMessage }) => ( - + {longMessage} )); } else { showExtraErrors = ( - + { setLocalState((prevState: WorkspaceState) => ({ @@ -414,6 +416,7 @@ export const InnerVisualizationWrapper = ({ expandError: !prevState.expandError, })); }} + data-test-subj="configuration-failure-more-errors" > {i18n.translate('xpack.lens.editorFrame.configurationFailureMoreErrors', { defaultMessage: ` +{errors} {errors, plural, one {error} other {errors}}`, @@ -445,7 +448,7 @@ export const InnerVisualizationWrapper = ({ - + {localState.configurationValidationError[0].longMessage} {showExtraErrors} @@ -477,7 +480,7 @@ export const InnerVisualizationWrapper = ({ padding="m" expression={expression!} searchContext={context} - reload$={autoRefreshFetch$} + searchSessionId={framePublicAPI.searchSessionId} onEvent={onEvent} onData$={onData$} renderMode="edit" diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx index b00760e9664f3..ea7ce99e92cef 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx @@ -260,6 +260,7 @@ export class Embeddable handleEvent={this.handleEvent} onData$={this.updateActiveData} renderMode={input.renderMode} + syncColors={input.syncColors} hasCompatibleActions={this.hasCompatibleActions} />, domNode diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/expression_wrapper.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/expression_wrapper.tsx index e2607886a4219..c91ca74b54a4f 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/expression_wrapper.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/expression_wrapper.tsx @@ -29,6 +29,7 @@ export interface ExpressionWrapperProps { inspectorAdapters?: Partial | undefined ) => void; renderMode?: RenderMode; + syncColors?: boolean; hasCompatibleActions?: ReactExpressionRendererProps['hasCompatibleActions']; } @@ -41,6 +42,7 @@ export function ExpressionWrapper({ searchSessionId, onData$, renderMode, + syncColors, hasCompatibleActions, }: ExpressionWrapperProps) { return ( @@ -70,6 +72,7 @@ export function ExpressionWrapper({ searchSessionId={searchSessionId} onData$={onData$} renderMode={renderMode} + syncColors={syncColors} renderError={(errorMessage, error) => (
    diff --git a/x-pack/plugins/lens/public/editor_frame_service/mocks.tsx b/x-pack/plugins/lens/public/editor_frame_service/mocks.tsx index 5ab410a1c0af2..2152c18ffeda4 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/mocks.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/mocks.tsx @@ -132,6 +132,7 @@ export function createMockFramePublicAPI(): FrameMock { get: () => palette, getAll: () => [palette], }, + searchSessionId: 'sessionId', }; } diff --git a/x-pack/plugins/lens/public/editor_frame_service/service.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/service.test.tsx index e9f8013ef7e2d..3687e0cce2f1d 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/service.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/service.test.tsx @@ -57,6 +57,7 @@ describe('editor_frame service', () => { indexPatternId: '1', fieldName: 'test', }, + searchSessionId: 'sessionId', }); instance.unmount(); })() @@ -78,6 +79,7 @@ describe('editor_frame service', () => { query: { query: '', language: 'lucene' }, filters: [], showNoDataPopover: jest.fn(), + searchSessionId: 'sessionId', }); instance.unmount(); diff --git a/x-pack/plugins/lens/public/editor_frame_service/service.tsx b/x-pack/plugins/lens/public/editor_frame_service/service.tsx index 0562e9bf4d32e..d4e9522f3bed1 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/service.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/service.tsx @@ -138,6 +138,7 @@ export class EditorFrameService { onChange, showNoDataPopover, initialContext, + searchSessionId, } ) => { domElement = element; @@ -172,6 +173,7 @@ export class EditorFrameService { onChange={onChange} showNoDataPopover={showNoDataPopover} initialContext={initialContext} + searchSessionId={searchSessionId} /> , domElement diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx index c655fc18ab5fa..cc22cbbf57883 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx @@ -16,6 +16,7 @@ import { EuiListGroupItemProps, EuiFormLabel, EuiToolTip, + EuiText, } from '@elastic/eui'; import { IndexPatternDimensionEditorProps } from './dimension_panel'; import { OperationSupportMatrix } from './operation_support'; @@ -37,6 +38,7 @@ import { BucketNestingEditor } from './bucket_nesting_editor'; import { IndexPattern, IndexPatternLayer } from '../types'; import { trackUiEvent } from '../../lens_ui_telemetry'; import { FormatSelector } from './format_selector'; +import { ReferenceEditor } from './reference_editor'; import { TimeScaling } from './time_scaling'; const operationPanels = getOperationDisplay(); @@ -156,7 +158,10 @@ export function DimensionEditor(props: DimensionEditorProps) { (selectedColumn && !hasField(selectedColumn) && definition.input === 'none'), disabledStatus: definition.getDisabledStatus && - definition.getDisabledStatus(state.indexPatterns[state.currentIndexPatternId]), + definition.getDisabledStatus( + state.indexPatterns[state.currentIndexPatternId], + state.layers[layerId] + ), }; }); @@ -180,7 +185,15 @@ export function DimensionEditor(props: DimensionEditorProps) { } let label: EuiListGroupItemProps['label'] = operationPanels[operationType].displayName; - if (disabledStatus) { + if (isActive && disabledStatus) { + label = ( + + + {operationPanels[operationType].displayName} + + + ); + } else if (disabledStatus) { label = ( {operationPanels[operationType].displayName} @@ -202,9 +215,12 @@ export function DimensionEditor(props: DimensionEditorProps) { compatibleWithCurrentField ? '' : ' incompatible' }`, onClick() { - if (operationDefinitionMap[operationType].input === 'none') { + if ( + operationDefinitionMap[operationType].input === 'none' || + operationDefinitionMap[operationType].input === 'fullReference' + ) { + // Clear invalid state because we are reseting to a valid column if (selectedColumn?.operationType === operationType) { - // Clear invalid state because we are reseting to a valid column if (incompleteInfo) { setStateWrapper(resetIncomplete(state.layers[layerId], columnId)); } @@ -291,6 +307,35 @@ export function DimensionEditor(props: DimensionEditorProps) {
    + {!incompleteInfo && + selectedColumn && + 'references' in selectedColumn && + selectedOperationDefinition?.input === 'fullReference' ? ( + <> + {selectedColumn.references.map((referenceId, index) => { + const validation = selectedOperationDefinition.requiredReferences[index]; + + return ( + { + setState(mergeLayer({ state, layerId, newLayer })); + }} + validation={validation} + currentIndexPattern={currentIndexPattern} + existingFields={state.existingFields} + selectionStyle={selectedOperationDefinition.selectionStyle} + dateRange={dateRange} + {...services} + /> + ); + })} + + + ) : null} + {!selectedColumn || selectedOperationDefinition?.input === 'field' || (incompleteOperation && operationDefinitionMap[incompleteOperation].input === 'field') ? ( @@ -325,7 +370,13 @@ export function DimensionEditor(props: DimensionEditorProps) { } incompleteOperation={incompleteOperation} onDeleteColumn={() => { - setStateWrapper(deleteColumn({ layer: state.layers[layerId], columnId })); + setStateWrapper( + deleteColumn({ + layer: state.layers[layerId], + columnId, + indexPattern: currentIndexPattern, + }) + ); }} onChoose={(choice) => { setStateWrapper( @@ -342,15 +393,6 @@ export function DimensionEditor(props: DimensionEditorProps) { ) : null} - {!currentFieldIsInvalid && !incompleteInfo && selectedColumn && ( - - )} - {!currentFieldIsInvalid && !incompleteInfo && selectedColumn && ParamEditor && ( <> )} + + {!currentFieldIsInvalid && !incompleteInfo && selectedColumn && ( + + )}
    @@ -432,11 +483,11 @@ export function DimensionEditor(props: DimensionEditorProps) { } function getErrorMessage( selectedColumn: IndexPatternColumn | undefined, - incompatibleSelectedOperationType: boolean, + incompleteOperation: boolean, input: 'none' | 'field' | 'fullReference' | undefined, fieldInvalid: boolean ) { - if (selectedColumn && incompatibleSelectedOperationType) { + if (selectedColumn && incompleteOperation) { if (input === 'field') { return i18n.translate('xpack.lens.indexPattern.invalidOperationLabel', { defaultMessage: 'To use this function, select a different field.', diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx index 6bfeafd41c6b4..5d477d98d042d 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx @@ -854,6 +854,7 @@ describe('IndexPatternDimensionEditorPanel', () => { dataType: 'date', isBucketed: true, label: '', + customLabel: true, operationType: 'date_histogram', sourceField: 'ts', params: { @@ -872,6 +873,7 @@ describe('IndexPatternDimensionEditorPanel', () => { columnId: 'col2', }; } + it('should not show custom options if time scaling is not available', () => { wrapper = mount( { layers: { first: { ...state.layers.first, + columnOrder: ['col1', 'col2'], columns: { ...state.layers.first.columns, col2: expect.objectContaining({ - sourceField: 'bytes', operationType: 'avg', - // Other parts of this don't matter for this test + sourceField: 'bytes', }), }, - columnOrder: ['col1', 'col2'], + incompleteColumns: {}, }, }, }, @@ -1237,7 +1239,9 @@ describe('IndexPatternDimensionEditorPanel', () => { it('should indicate compatible fields when selecting the operation first', () => { wrapper = mount(); - wrapper.find('button[data-test-subj="lns-indexPatternDimension-avg"]').simulate('click'); + act(() => { + wrapper.find('button[data-test-subj="lns-indexPatternDimension-avg"]').simulate('click'); + }); const options = wrapper .find(EuiComboBox) @@ -1317,12 +1321,18 @@ describe('IndexPatternDimensionEditorPanel', () => { expect(items.map(({ label }: { label: React.ReactNode }) => label)).toEqual([ 'Average', 'Count', + 'Counter rate', + 'Cumulative sum', + 'Differences', 'Last value', 'Maximum', 'Median', 'Minimum', + 'Moving average', + 'Percentile', 'Sum', 'Unique count', + '\u00a0', ]); }); @@ -1536,4 +1546,101 @@ describe('IndexPatternDimensionEditorPanel', () => { }, }); }); + + it('should hide the top level field selector when switching from non-reference to reference', () => { + wrapper = mount(); + + expect(wrapper.find('ReferenceEditor')).toHaveLength(0); + + wrapper + .find('button[data-test-subj="lns-indexPatternDimension-derivative incompatible"]') + .simulate('click'); + + expect(wrapper.find('ReferenceEditor')).toHaveLength(1); + }); + + it('should hide the reference editors when switching from reference to non-reference', () => { + const stateWithReferences: IndexPatternPrivateState = getStateWithColumns({ + col1: { + label: 'Differences of (incomplete)', + dataType: 'number', + isBucketed: false, + operationType: 'derivative', + references: ['col2'], + params: {}, + }, + }); + + wrapper = mount( + + ); + + expect(wrapper.find('ReferenceEditor')).toHaveLength(1); + + wrapper + .find('button[data-test-subj="lns-indexPatternDimension-avg incompatible"]') + .simulate('click'); + + expect(wrapper.find('ReferenceEditor')).toHaveLength(0); + }); + + it('should show a warning when the current dimension is no longer configurable', () => { + const stateWithInvalidCol: IndexPatternPrivateState = getStateWithColumns({ + col1: { + label: 'Invalid derivative', + dataType: 'number', + isBucketed: false, + operationType: 'derivative', + references: ['ref1'], + }, + }); + + wrapper = mount( + + ); + + expect( + wrapper + .find('[data-test-subj="lns-indexPatternDimension-derivative incompatible"]') + .find('EuiText[color="danger"]') + .first() + ).toBeTruthy(); + }); + + it('should remove options to select references when there are no time fields', () => { + const stateWithoutTime: IndexPatternPrivateState = { + ...getStateWithColumns({ + col1: { + label: 'Avg', + dataType: 'number', + isBucketed: false, + operationType: 'avg', + sourceField: 'bytes', + }, + }), + indexPatterns: { + 1: { + id: '1', + title: 'my-fake-index-pattern', + hasRestrictions: false, + fields, + getFieldByName: getFieldByNameFactory([ + { + name: 'bytes', + displayName: 'bytes', + type: 'number', + aggregatable: true, + searchable: true, + }, + ]), + }, + }, + }; + + wrapper = mount( + + ); + + expect(wrapper.find('[data-test-subj="lns-indexPatternDimension-derivative"]')).toHaveLength(0); + }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx index 406a32f62b2c7..fbdf90e6cc4c7 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx @@ -41,6 +41,7 @@ export interface FieldSelectProps extends EuiComboBoxProps<{}> { onDeleteColumn: () => void; existingFields: IndexPatternPrivateState['existingFields']; fieldIsInvalid: boolean; + markAllFieldsCompatible?: boolean; } export function FieldSelect({ @@ -53,6 +54,7 @@ export function FieldSelect({ onDeleteColumn, existingFields, fieldIsInvalid, + markAllFieldsCompatible, ...rest }: FieldSelectProps) { const { operationByField } = operationSupportMatrix; @@ -93,7 +95,7 @@ export function FieldSelect({ : operationByField[field]!.values().next().value, }, exists: containsData(field), - compatible: isCompatibleWithCurrentOperation(field), + compatible: markAllFieldsCompatible || isCompatibleWithCurrentOperation(field), }; }) .sort((a, b) => { @@ -163,6 +165,7 @@ export function FieldSelect({ currentIndexPattern, operationByField, existingFields, + markAllFieldsCompatible, ]); return ( diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/operation_support.ts b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/operation_support.ts index 817fdf637f001..9d55a9d5f7522 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/operation_support.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/operation_support.ts @@ -49,7 +49,7 @@ export const getOperationSupportMatrix = (props: Props): OperationSupportMatrix supportedFieldsByOperation[operation.operationType] = new Set(); } supportedFieldsByOperation[operation.operationType]?.add(operation.field); - } else if (operation.type === 'none') { + } else { supportedOperationsWithoutField.add(operation.operationType); } }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.test.tsx new file mode 100644 index 0000000000000..0891dd27fcf17 --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.test.tsx @@ -0,0 +1,436 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { ReactWrapper, ShallowWrapper } from 'enzyme'; +import { act } from 'react-dom/test-utils'; +import { EuiComboBox } from '@elastic/eui'; +import { mountWithIntl as mount } from '@kbn/test/jest'; +import type { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'kibana/public'; +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; +import type { DataPublicPluginStart } from 'src/plugins/data/public'; +import { OperationMetadata } from '../../types'; +import { createMockedIndexPattern } from '../mocks'; +import { ReferenceEditor, ReferenceEditorProps } from './reference_editor'; +import { insertOrReplaceColumn } from '../operations'; +import { FieldSelect } from './field_select'; + +jest.mock('../operations'); + +describe('reference editor', () => { + let wrapper: ReactWrapper | ShallowWrapper; + let updateLayer: jest.Mock; + + function getDefaultArgs() { + return { + layer: { + indexPatternId: '1', + columns: {}, + columnOrder: [], + }, + columnId: 'ref', + updateLayer, + selectionStyle: 'full' as const, + currentIndexPattern: createMockedIndexPattern(), + existingFields: { + 'my-fake-index-pattern': { + timestamp: true, + bytes: true, + memory: true, + source: true, + }, + }, + dateRange: { fromDate: 'now-1d', toDate: 'now' }, + storage: {} as IStorageWrapper, + uiSettings: {} as IUiSettingsClient, + savedObjectsClient: {} as SavedObjectsClientContract, + http: {} as HttpSetup, + data: {} as DataPublicPluginStart, + }; + } + + beforeEach(() => { + updateLayer = jest.fn().mockImplementation((newLayer) => { + if (wrapper instanceof ReactWrapper) { + wrapper.setProps({ layer: newLayer }); + } + }); + + jest.clearAllMocks(); + }); + + afterEach(() => { + if (wrapper) { + wrapper.unmount(); + } + }); + + it('should indicate that all functions and available fields are compatible in the empty state', () => { + wrapper = mount( + meta.dataType === 'number', + }} + /> + ); + + const functions = wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-reference-function"]') + .prop('options'); + + expect(functions).not.toContainEqual( + expect.objectContaining({ 'data-test-subj': expect.stringContaining('Incompatible') }) + ); + + const fields = wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-dimension-field"]') + .prop('options'); + + expect(fields![0].options).not.toContainEqual( + expect.objectContaining({ 'data-test-subj': expect.stringContaining('Incompatible') }) + ); + expect(fields![1].options).not.toContainEqual( + expect.objectContaining({ 'data-test-subj': expect.stringContaining('Incompatible') }) + ); + }); + + it('should indicate functions and fields that are incompatible with the current', () => { + wrapper = mount( + meta.isBucketed, + }} + /> + ); + + const functions = wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-reference-function"]') + .prop('options'); + expect(functions.find(({ label }) => label === 'Date histogram')!['data-test-subj']).toContain( + 'incompatible' + ); + + const fields = wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-dimension-field"]') + .prop('options'); + expect( + fields![0].options!.find(({ label }) => label === 'timestampLabel')!['data-test-subj'] + ).toContain('Incompatible'); + }); + + it('should not update when selecting the same operation', () => { + wrapper = mount( + meta.dataType === 'number', + }} + /> + ); + + const comboBox = wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-reference-function"]'); + const option = comboBox.prop('options')!.find(({ label }) => label === 'Average')!; + + act(() => { + comboBox.prop('onChange')!([option]); + }); + expect(insertOrReplaceColumn).not.toHaveBeenCalled(); + }); + + it('should keep the field when replacing an existing reference with a compatible function', () => { + wrapper = mount( + meta.dataType === 'number', + }} + /> + ); + + const comboBox = wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-reference-function"]'); + const option = comboBox.prop('options')!.find(({ label }) => label === 'Maximum')!; + + act(() => { + comboBox.prop('onChange')!([option]); + }); + + expect(insertOrReplaceColumn).toHaveBeenCalledWith( + expect.objectContaining({ + op: 'max', + field: expect.objectContaining({ name: 'bytes' }), + }) + ); + }); + + it('should transition to another function with incompatible field', () => { + wrapper = mount( + true, + }} + /> + ); + + const comboBox = wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-reference-function"]'); + const option = comboBox.prop('options')!.find(({ label }) => label === 'Date histogram')!; + + act(() => { + comboBox.prop('onChange')!([option]); + }); + + expect(insertOrReplaceColumn).toHaveBeenCalledWith( + expect.objectContaining({ + op: 'date_histogram', + field: undefined, + }) + ); + }); + + it('should hide the function selector when using a field-only selection style', () => { + wrapper = mount( + true, + }} + /> + ); + + const comboBox = wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-reference-function"]'); + expect(comboBox).toHaveLength(0); + }); + + it('should pass the incomplete operation info to FieldSelect', () => { + wrapper = mount( + true, + }} + /> + ); + + const fieldSelect = wrapper.find(FieldSelect); + expect(fieldSelect.prop('fieldIsInvalid')).toEqual(true); + expect(fieldSelect.prop('selectedField')).toEqual('bytes'); + expect(fieldSelect.prop('selectedOperationType')).toEqual('avg'); + expect(fieldSelect.prop('incompleteOperation')).toEqual('max'); + expect(fieldSelect.prop('markAllFieldsCompatible')).toEqual(false); + }); + + it('should pass the incomplete field info to FieldSelect', () => { + wrapper = mount( + true, + }} + /> + ); + + const fieldSelect = wrapper.find(FieldSelect); + expect(fieldSelect.prop('fieldIsInvalid')).toEqual(false); + expect(fieldSelect.prop('selectedField')).toEqual('timestamp'); + expect(fieldSelect.prop('selectedOperationType')).toEqual('avg'); + expect(fieldSelect.prop('incompleteOperation')).toBeUndefined(); + }); + + it('should show the FieldSelect as invalid in the empty state for field-only forms', () => { + wrapper = mount( + true, + }} + /> + ); + + const fieldSelect = wrapper.find(FieldSelect); + expect(fieldSelect.prop('fieldIsInvalid')).toEqual(true); + expect(fieldSelect.prop('selectedField')).toBeUndefined(); + expect(fieldSelect.prop('selectedOperationType')).toBeUndefined(); + expect(fieldSelect.prop('incompleteOperation')).toBeUndefined(); + expect(fieldSelect.prop('markAllFieldsCompatible')).toEqual(true); + }); + + it('should show the ParamEditor for functions that offer one', () => { + wrapper = mount( + true, + }} + /> + ); + + expect(wrapper.find('[data-test-subj="lns-indexPattern-lastValue-sortField"]').exists()).toBe( + true + ); + }); + + it('should hide the ParamEditor for incomplete functions', () => { + wrapper = mount( + true, + }} + /> + ); + + expect(wrapper.find('[data-test-subj="lns-indexPattern-lastValue-sortField"]').exists()).toBe( + false + ); + }); +}); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.tsx new file mode 100644 index 0000000000000..d73530ec8a920 --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.tsx @@ -0,0 +1,306 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import './dimension_editor.scss'; +import _ from 'lodash'; +import React, { useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiFormRow, EuiSpacer, EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; +import type { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'kibana/public'; +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; +import type { DataPublicPluginStart } from 'src/plugins/data/public'; +import type { DateRange } from '../../../common'; +import type { OperationSupportMatrix } from './operation_support'; +import type { OperationType } from '../indexpattern'; +import { + operationDefinitionMap, + getOperationDisplay, + insertOrReplaceColumn, + deleteColumn, + isOperationAllowedAsReference, + FieldBasedIndexPatternColumn, + RequiredReference, +} from '../operations'; +import { FieldSelect } from './field_select'; +import { hasField } from '../utils'; +import type { IndexPattern, IndexPatternLayer, IndexPatternPrivateState } from '../types'; +import { trackUiEvent } from '../../lens_ui_telemetry'; + +const operationPanels = getOperationDisplay(); + +export interface ReferenceEditorProps { + layer: IndexPatternLayer; + selectionStyle: 'full' | 'field'; + validation: RequiredReference; + columnId: string; + updateLayer: (newLayer: IndexPatternLayer) => void; + currentIndexPattern: IndexPattern; + existingFields: IndexPatternPrivateState['existingFields']; + dateRange: DateRange; + + // Services + uiSettings: IUiSettingsClient; + storage: IStorageWrapper; + savedObjectsClient: SavedObjectsClientContract; + http: HttpSetup; + data: DataPublicPluginStart; +} + +export function ReferenceEditor(props: ReferenceEditorProps) { + const { + layer, + columnId, + updateLayer, + currentIndexPattern, + existingFields, + validation, + selectionStyle, + dateRange, + ...services + } = props; + + const column = layer.columns[columnId]; + const selectedOperationDefinition = column && operationDefinitionMap[column.operationType]; + + const ParamEditor = selectedOperationDefinition?.paramEditor; + + const incompleteInfo = layer.incompleteColumns ? layer.incompleteColumns[columnId] : undefined; + const incompleteOperation = incompleteInfo?.operationType; + const incompleteField = incompleteInfo?.sourceField ?? null; + + // Basically the operation support matrix, but different validation + const operationSupportMatrix: OperationSupportMatrix & { + operationTypes: Set; + } = useMemo(() => { + const operationTypes: Set = new Set(); + const operationWithoutField: Set = new Set(); + const operationByField: Partial>> = {}; + const fieldByOperation: Partial>> = {}; + Object.values(operationDefinitionMap) + .sort((op1, op2) => { + return op1.displayName.localeCompare(op2.displayName); + }) + .forEach((op) => { + if (op.input === 'field') { + const allFields = currentIndexPattern.fields.filter((field) => + isOperationAllowedAsReference({ + operationType: op.type, + validation, + field, + indexPattern: currentIndexPattern, + }) + ); + if (allFields.length) { + operationTypes.add(op.type); + fieldByOperation[op.type] = new Set(allFields.map(({ name }) => name)); + allFields.forEach((field) => { + if (!operationByField[field.name]) { + operationByField[field.name] = new Set(); + } + operationByField[field.name]?.add(op.type); + }); + } + } else if ( + isOperationAllowedAsReference({ + operationType: op.type, + validation, + indexPattern: currentIndexPattern, + }) + ) { + operationTypes.add(op.type); + operationWithoutField.add(op.type); + } + }); + return { + operationTypes, + operationWithoutField, + operationByField, + fieldByOperation, + }; + }, [currentIndexPattern, validation]); + + const functionOptions: Array> = Array.from( + operationSupportMatrix.operationTypes + ).map((operationType) => { + const def = operationDefinitionMap[operationType]; + const label = operationPanels[operationType].displayName; + const isCompatible = + !column || + (column && + hasField(column) && + def.input === 'field' && + operationSupportMatrix.fieldByOperation[operationType]?.has(column.sourceField)) || + (column && !hasField(column) && def.input !== 'field'); + + return { + label, + value: operationType, + className: 'lnsIndexPatternDimensionEditor__operation', + 'data-test-subj': `lns-indexPatternDimension-${operationType}${ + isCompatible ? '' : ' incompatible' + }`, + }; + }); + + function onChooseFunction(operationType: OperationType) { + if (column?.operationType === operationType) { + return; + } + const possibleFieldNames = operationSupportMatrix.fieldByOperation[operationType]; + if (column && 'sourceField' in column && possibleFieldNames?.has(column.sourceField)) { + // Reuse the current field if possible + updateLayer( + insertOrReplaceColumn({ + layer, + columnId, + op: operationType, + indexPattern: currentIndexPattern, + field: currentIndexPattern.getFieldByName(column.sourceField), + }) + ); + } else { + // If reusing the field is impossible, we generally can't choose for the user. + // The one exception is if the field is the only possible field, like Count of Records. + const possibleField = + possibleFieldNames?.size === 1 + ? currentIndexPattern.getFieldByName(possibleFieldNames.values().next().value) + : undefined; + + updateLayer( + insertOrReplaceColumn({ + layer, + columnId, + op: operationType, + indexPattern: currentIndexPattern, + field: possibleField, + }) + ); + } + trackUiEvent(`indexpattern_dimension_operation_${operationType}`); + return; + } + + const selectedOption = incompleteInfo?.operationType + ? [functionOptions.find(({ value }) => value === incompleteInfo.operationType)!] + : column + ? [functionOptions.find(({ value }) => value === column.operationType)!] + : []; + + // If the operationType is incomplete, the user needs to select a field- so + // the function is marked as valid. + const showOperationInvalid = !column && !Boolean(incompleteInfo?.operationType); + // The field is invalid if the operation has been updated without a field, + // or if we are in a field-only mode but empty state + const showFieldInvalid = + Boolean(incompleteInfo?.operationType) || (selectionStyle === 'field' && !column); + + return ( +
    +
    + {selectionStyle !== 'field' ? ( + <> + + { + if (choices.length === 0) { + updateLayer( + deleteColumn({ layer, columnId, indexPattern: currentIndexPattern }) + ); + return; + } + + trackUiEvent('indexpattern_dimension_field_changed'); + + onChooseFunction(choices[0].value!); + }} + /> + + + + ) : null} + + {!column || selectedOperationDefinition.input === 'field' ? ( + + { + updateLayer(deleteColumn({ layer, columnId, indexPattern: currentIndexPattern })); + }} + onChoose={(choice) => { + updateLayer( + insertOrReplaceColumn({ + layer, + columnId, + indexPattern: currentIndexPattern, + op: choice.operationType, + field: currentIndexPattern.getFieldByName(choice.field), + }) + ); + }} + /> + + ) : null} + + {column && !incompleteInfo && ParamEditor && ( + <> + + + )} +
    +
    + ); +} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts index 2e55abf4a429a..1f23fd3830477 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts @@ -474,6 +474,53 @@ describe('IndexPattern Data Source', () => { expect(ast.chain[0].arguments.timeFields).toEqual(['timestamp', 'another_datefield']); }); + it('should add the suffix to the remap column id if provided by the operation', async () => { + const queryBaseState: IndexPatternBaseState = { + currentIndexPatternId: '1', + layers: { + first: { + indexPatternId: '1', + columnOrder: ['def', 'abc'], + columns: { + abc: { + label: '23rd percentile', + dataType: 'number', + isBucketed: false, + sourceField: 'bytes', + operationType: 'percentile', + params: { + percentile: 23, + }, + }, + def: { + label: 'Terms', + dataType: 'string', + isBucketed: true, + operationType: 'terms', + sourceField: 'source', + params: { + size: 5, + orderBy: { + type: 'alphabetical', + }, + orderDirection: 'asc', + }, + }, + }, + }, + }, + }; + + const state = enrichBaseState(queryBaseState); + + const ast = indexPatternDatasource.toExpression(state, 'first') as Ast; + expect(Object.keys(JSON.parse(ast.chain[1].arguments.idMap[0] as string))).toEqual([ + 'col-0-def', + // col-1 is the auto naming of esasggs, abc is the specified column id, .23 is the generated suffix + 'col-1-abc.23', + ]); + }); + it('should add time_scale and format function if time scale is set and supported', async () => { const queryBaseState: IndexPatternBaseState = { currentIndexPatternId: '1', @@ -858,165 +905,49 @@ describe('IndexPattern Data Source', () => { it('should return null for non-existant columns', () => { expect(publicAPI.getOperationForColumnId('col2')).toBe(null); }); - }); - }); - describe('#getErrorMessages', () => { - it('should detect a missing reference in a layer', () => { - const state = { - indexPatternRefs: [], - existingFields: {}, - isFirstExistenceFetch: false, - indexPatterns: expectedIndexPatterns, - layers: { - first: { - indexPatternId: '1', - columnOrder: ['col1'], - columns: { - col1: { - dataType: 'number', - isBucketed: false, - label: 'Foo', - operationType: 'count', // <= invalid - sourceField: 'bytes', - }, - }, - }, - }, - currentIndexPatternId: '1', - }; - const messages = indexPatternDatasource.getErrorMessages(state as IndexPatternPrivateState); - expect(messages).toHaveLength(1); - expect(messages![0]).toEqual({ - shortMessage: 'Invalid reference.', - longMessage: '"Foo" has an invalid reference.', - }); - }); - - it('should detect and batch missing references in a layer', () => { - const state = { - indexPatternRefs: [], - existingFields: {}, - isFirstExistenceFetch: false, - indexPatterns: expectedIndexPatterns, - layers: { - first: { - indexPatternId: '1', - columnOrder: ['col1', 'col2'], - columns: { - col1: { - dataType: 'number', - isBucketed: false, - label: 'Foo', - operationType: 'count', // <= invalid - sourceField: 'bytes', - }, - col2: { - dataType: 'number', - isBucketed: false, - label: 'Foo2', - operationType: 'count', // <= invalid - sourceField: 'memory', - }, - }, - }, - }, - currentIndexPatternId: '1', - }; - const messages = indexPatternDatasource.getErrorMessages(state as IndexPatternPrivateState); - expect(messages).toHaveLength(1); - expect(messages![0]).toEqual({ - shortMessage: 'Invalid references.', - longMessage: '"Foo", "Foo2" have invalid reference.', - }); - }); + it('should return null for referenced columns', () => { + publicAPI = indexPatternDatasource.getPublicAPI({ + state: { + ...enrichBaseState(baseState), + layers: { + first: { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + col1: { + label: 'Sum', + dataType: 'number', + isBucketed: false, - it('should detect and batch missing references in multiple layers', () => { - const state = { - indexPatternRefs: [], - existingFields: {}, - isFirstExistenceFetch: false, - indexPatterns: expectedIndexPatterns, - layers: { - first: { - indexPatternId: '1', - columnOrder: ['col1', 'col2'], - columns: { - col1: { - dataType: 'number', - isBucketed: false, - label: 'Foo', - operationType: 'count', // <= invalid - sourceField: 'bytes', - }, - col2: { - dataType: 'number', - isBucketed: false, - label: 'Foo2', - operationType: 'count', // <= invalid - sourceField: 'memory', - }, - }, - }, - second: { - indexPatternId: '1', - columnOrder: ['col1'], - columns: { - col1: { - dataType: 'string', - isBucketed: false, - label: 'Foo', - operationType: 'count', // <= invalid - sourceField: 'source', - }, - }, - }, - }, - currentIndexPatternId: '1', - }; - const messages = indexPatternDatasource.getErrorMessages(state as IndexPatternPrivateState); - expect(messages).toHaveLength(2); - expect(messages).toEqual([ - { - shortMessage: 'Invalid references on Layer 1.', - longMessage: 'Layer 1 has invalid references in "Foo", "Foo2".', - }, - { - shortMessage: 'Invalid reference on Layer 2.', - longMessage: 'Layer 2 has an invalid reference in "Foo".', - }, - ]); - }); + operationType: 'sum', + sourceField: 'test', + params: {}, + } as IndexPatternColumn, + col2: { + label: 'Cumulative sum', + dataType: 'number', + isBucketed: false, - it('should return no errors if all references are satified', () => { - const state = { - indexPatternRefs: [], - existingFields: {}, - isFirstExistenceFetch: false, - indexPatterns: expectedIndexPatterns, - layers: { - first: { - indexPatternId: '1', - columnOrder: ['col1'], - columns: { - col1: { - dataType: 'number', - isBucketed: false, - label: 'Foo', - operationType: 'avg', - sourceField: 'bytes', + operationType: 'cumulative_sum', + references: ['col1'], + params: {}, + } as IndexPatternColumn, + }, }, }, }, - }, - currentIndexPatternId: '1', - }; - expect( - indexPatternDatasource.getErrorMessages(state as IndexPatternPrivateState) - ).toBeUndefined(); + layerId: 'first', + }); + expect(publicAPI.getOperationForColumnId('col1')).toEqual(null); + }); }); + }); - it('should return no errors with layers with no columns', () => { + describe('#getErrorMessages', () => { + it('should use the results of getErrorMessages directly when single layer', () => { + (getErrorMessages as jest.Mock).mockClear(); + (getErrorMessages as jest.Mock).mockReturnValueOnce(['error 1', 'error 2']); const state: IndexPatternPrivateState = { indexPatternRefs: [], existingFields: {}, @@ -1031,10 +962,14 @@ describe('IndexPattern Data Source', () => { }, currentIndexPatternId: '1', }; - expect(indexPatternDatasource.getErrorMessages(state)).toBeUndefined(); + expect(indexPatternDatasource.getErrorMessages(state)).toEqual([ + { longMessage: 'error 1', shortMessage: '' }, + { longMessage: 'error 2', shortMessage: '' }, + ]); + expect(getErrorMessages).toHaveBeenCalledTimes(1); }); - it('should bubble up invalid configuration from operations', () => { + it('should prepend each error with its layer number on multi-layer chart', () => { (getErrorMessages as jest.Mock).mockClear(); (getErrorMessages as jest.Mock).mockReturnValueOnce(['error 1', 'error 2']); const state: IndexPatternPrivateState = { @@ -1048,14 +983,19 @@ describe('IndexPattern Data Source', () => { columnOrder: [], columns: {}, }, + second: { + indexPatternId: '1', + columnOrder: [], + columns: {}, + }, }, currentIndexPatternId: '1', }; expect(indexPatternDatasource.getErrorMessages(state)).toEqual([ - { shortMessage: 'error 1', longMessage: '' }, - { shortMessage: 'error 2', longMessage: '' }, + { longMessage: 'Layer 1 error: error 1', shortMessage: '' }, + { longMessage: 'Layer 1 error: error 2', shortMessage: '' }, ]); - expect(getErrorMessages).toHaveBeenCalledTimes(1); + expect(getErrorMessages).toHaveBeenCalledTimes(2); }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx index 2937b1cf05760..6c6bd2e1bb439 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx @@ -39,12 +39,7 @@ import { getDatasourceSuggestionsForVisualizeField, } from './indexpattern_suggestions'; -import { - getInvalidColumnsForLayer, - getInvalidLayers, - isDraggedField, - normalizeOperationDataType, -} from './utils'; +import { isDraggedField, normalizeOperationDataType } from './utils'; import { LayerPanel } from './layerpanel'; import { IndexPatternColumn, getErrorMessages, IncompleteColumn } from './operations'; import { IndexPatternField, IndexPatternPrivateState, IndexPatternPersistedState } from './types'; @@ -55,7 +50,6 @@ import { mergeLayer } from './state_helpers'; import { Datasource, StateSetter } from '../index'; import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public'; import { deleteColumn, isReferenced } from './operations'; -import { FieldBasedIndexPatternColumn } from './operations/definitions/column_types'; import { Dragging } from '../drag_drop/providers'; export { OperationType, IndexPatternColumn, deleteColumn } from './operations'; @@ -162,10 +156,11 @@ export function getIndexPatternDatasource({ }, removeColumn({ prevState, layerId, columnId }) { + const indexPattern = prevState.indexPatterns[prevState.layers[layerId]?.indexPatternId]; return mergeLayer({ state: prevState, layerId, - newLayer: deleteColumn({ layer: prevState.layers[layerId], columnId }), + newLayer: deleteColumn({ layer: prevState.layers[layerId], columnId, indexPattern }), }); }, @@ -351,7 +346,9 @@ export function getIndexPatternDatasource({ const layer = state.layers[layerId]; if (layer && layer.columns[columnId]) { - return columnToOperation(layer.columns[columnId], columnLabelMap[columnId]); + if (!isReferenced(layer, columnId)) { + return columnToOperation(layer.columns[columnId], columnLabelMap[columnId]); + } } return null; }, @@ -369,91 +366,46 @@ export function getIndexPatternDatasource({ if (!state) { return; } - const invalidLayers = getInvalidLayers(state); - const layerErrors = Object.values(state.layers).flatMap((layer) => + const layerErrors = Object.values(state.layers).map((layer) => (getErrorMessages(layer) ?? []).map((message) => ({ - shortMessage: message, - longMessage: '', + shortMessage: '', // Not displayed currently + longMessage: message, })) ); - if (invalidLayers.length === 0) { - return layerErrors.length ? layerErrors : undefined; + // Single layer case, no need to explain more + if (layerErrors.length <= 1) { + return layerErrors[0]?.length ? layerErrors[0] : undefined; } - const realIndex = Object.values(state.layers) - .map((layer, i) => { - const filteredIndex = invalidLayers.indexOf(layer); - if (filteredIndex > -1) { - return [filteredIndex, i + 1]; - } - }) - .filter(Boolean) as Array<[number, number]>; - const invalidColumnsForLayer: string[][] = getInvalidColumnsForLayer( - invalidLayers, - state.indexPatterns - ); - const originalLayersList = Object.keys(state.layers); - - if (layerErrors.length || realIndex.length) { - return [ - ...layerErrors, - ...realIndex.map(([filteredIndex, layerIndex]) => { - const columnLabelsWithBrokenReferences: string[] = invalidColumnsForLayer[ - filteredIndex - ].map((columnId) => { - const column = invalidLayers[filteredIndex].columns[ - columnId - ] as FieldBasedIndexPatternColumn; - return column.label; - }); - - if (originalLayersList.length === 1) { - return { - shortMessage: i18n.translate( - 'xpack.lens.indexPattern.dataReferenceFailureShortSingleLayer', - { - defaultMessage: - 'Invalid {columns, plural, one {reference} other {references}}.', - values: { - columns: columnLabelsWithBrokenReferences.length, - }, - } - ), - longMessage: i18n.translate( - 'xpack.lens.indexPattern.dataReferenceFailureLongSingleLayer', - { - defaultMessage: `"{columns}" {columnsLength, plural, one {has an} other {have}} invalid reference.`, - values: { - columns: columnLabelsWithBrokenReferences.join('", "'), - columnsLength: columnLabelsWithBrokenReferences.length, - }, - } - ), - }; - } - return { - shortMessage: i18n.translate('xpack.lens.indexPattern.dataReferenceFailureShort', { - defaultMessage: - 'Invalid {columnsLength, plural, one {reference} other {references}} on Layer {layer}.', - values: { - layer: layerIndex, - columnsLength: columnLabelsWithBrokenReferences.length, - }, - }), - longMessage: i18n.translate('xpack.lens.indexPattern.dataReferenceFailureLong', { - defaultMessage: `Layer {layer} has {columnsLength, plural, one {an invalid} other {invalid}} {columnsLength, plural, one {reference} other {references}} in "{columns}".`, - values: { - layer: layerIndex, - columns: columnLabelsWithBrokenReferences.join('", "'), - columnsLength: columnLabelsWithBrokenReferences.length, - }, - }), - }; - }), - ]; - } + // For multiple layers we will prepend each error with the layer number + const messages = layerErrors.flatMap((errors, index) => { + return errors.map((error) => { + const { shortMessage, longMessage } = error; + return { + shortMessage: shortMessage + ? i18n.translate('xpack.lens.indexPattern.layerErrorWrapper', { + defaultMessage: 'Layer {position} error: {wrappedMessage}', + values: { + position: index + 1, + wrappedMessage: shortMessage, + }, + }) + : '', + longMessage: longMessage + ? i18n.translate('xpack.lens.indexPattern.layerErrorWrapper', { + defaultMessage: 'Layer {position} error: {wrappedMessage}', + values: { + position: index + 1, + wrappedMessage: longMessage, + }, + }) + : '', + }; + }); + }); + return messages.length ? messages : undefined; }, }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx index 9fbad553d441a..97a63de4f7ba2 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx @@ -6,11 +6,12 @@ import { DatasourceSuggestion } from '../types'; import { generateId } from '../id_generator'; -import { IndexPatternPrivateState } from './types'; +import type { IndexPatternPrivateState } from './types'; import { getDatasourceSuggestionsForField, getDatasourceSuggestionsFromCurrentState, getDatasourceSuggestionsForVisualizeField, + IndexPatternSuggestion, } from './indexpattern_suggestions'; import { documentField } from './document_field'; import { getFieldByNameFactory } from './pure_helpers'; @@ -153,6 +154,7 @@ function testInitialState(): IndexPatternPrivateState { columns: { col1: { label: 'My Op', + customLabel: true, dataType: 'string', isBucketed: true, @@ -172,6 +174,19 @@ function testInitialState(): IndexPatternPrivateState { }; } +// Simplifies the debug output for failed test +function getSuggestionSubset( + suggestions: IndexPatternSuggestion[] +): Array> { + return suggestions.map((s) => { + const newSuggestion = { ...s } as Omit & { + state?: IndexPatternPrivateState; + }; + delete newSuggestion.state; + return newSuggestion; + }); +} + describe('IndexPattern Data Source suggestions', () => { beforeEach(async () => { let count = 0; @@ -698,6 +713,7 @@ describe('IndexPattern Data Source suggestions', () => { isBucketed: true, sourceField: 'source', label: 'values of source', + customLabel: true, operationType: 'terms', params: { orderBy: { type: 'column', columnId: 'colb' }, @@ -710,6 +726,7 @@ describe('IndexPattern Data Source suggestions', () => { isBucketed: false, sourceField: 'bytes', label: 'Avg of bytes', + customLabel: true, operationType: 'avg', }, }, @@ -733,7 +750,7 @@ describe('IndexPattern Data Source suggestions', () => { dataType: 'date', isBucketed: true, sourceField: 'timestamp', - label: 'date histogram of timestamp', + label: 'timestamp', operationType: 'date_histogram', params: { interval: 'w', @@ -744,6 +761,7 @@ describe('IndexPattern Data Source suggestions', () => { isBucketed: false, sourceField: 'bytes', label: 'Avg of bytes', + customLabel: true, operationType: 'avg', }, }, @@ -782,6 +800,7 @@ describe('IndexPattern Data Source suggestions', () => { }); it('puts a date histogram column after the last bucket column on date field', () => { + (generateId as jest.Mock).mockReturnValue('newid'); const initialState = stateWithNonEmptyTables(); const suggestions = getDatasourceSuggestionsForField(initialState, '1', { name: 'timestamp', @@ -790,17 +809,16 @@ describe('IndexPattern Data Source suggestions', () => { aggregatable: true, searchable: true, }); - expect(suggestions).toContainEqual( expect.objectContaining({ state: expect.objectContaining({ layers: { previousLayer: initialState.layers.previousLayer, currentLayer: expect.objectContaining({ - columnOrder: ['cola', 'id1', 'colb'], + columnOrder: ['cola', 'newid', 'colb'], columns: { ...initialState.layers.currentLayer.columns, - id1: expect.objectContaining({ + newid: expect.objectContaining({ operationType: 'date_histogram', sourceField: 'timestamp', }), @@ -817,7 +835,7 @@ describe('IndexPattern Data Source suggestions', () => { columnId: 'cola', }), expect.objectContaining({ - columnId: 'id1', + columnId: 'newid', }), expect.objectContaining({ columnId: 'colb', @@ -845,6 +863,7 @@ describe('IndexPattern Data Source suggestions', () => { }); it('appends a terms column with default size on string field', () => { + (generateId as jest.Mock).mockReturnValue('newid'); const initialState = stateWithNonEmptyTables(); const suggestions = getDatasourceSuggestionsForField(initialState, '1', { name: 'dest', @@ -853,17 +872,16 @@ describe('IndexPattern Data Source suggestions', () => { aggregatable: true, searchable: true, }); - expect(suggestions).toContainEqual( expect.objectContaining({ state: expect.objectContaining({ layers: { previousLayer: initialState.layers.previousLayer, currentLayer: expect.objectContaining({ - columnOrder: ['cola', 'id1', 'colb'], + columnOrder: ['cola', 'newid', 'colb'], columns: { ...initialState.layers.currentLayer.columns, - id1: expect.objectContaining({ + newid: expect.objectContaining({ operationType: 'terms', sourceField: 'dest', params: expect.objectContaining({ size: 3 }), @@ -877,6 +895,7 @@ describe('IndexPattern Data Source suggestions', () => { }); it('suggests both replacing and adding metric if only one other metric is set', () => { + (generateId as jest.Mock).mockReturnValue('newid'); const initialState = stateWithNonEmptyTables(); const suggestions = getDatasourceSuggestionsForField(initialState, '1', { name: 'memory', @@ -885,7 +904,6 @@ describe('IndexPattern Data Source suggestions', () => { aggregatable: true, searchable: true, }); - expect(suggestions).toContainEqual( expect.objectContaining({ state: expect.objectContaining({ @@ -910,11 +928,11 @@ describe('IndexPattern Data Source suggestions', () => { state: expect.objectContaining({ layers: expect.objectContaining({ currentLayer: expect.objectContaining({ - columnOrder: ['cola', 'colb', 'id1'], + columnOrder: ['cola', 'colb', 'newid'], columns: { cola: initialState.layers.currentLayer.columns.cola, colb: initialState.layers.currentLayer.columns.colb, - id1: expect.objectContaining({ + newid: expect.objectContaining({ operationType: 'avg', sourceField: 'memory', }), @@ -927,6 +945,7 @@ describe('IndexPattern Data Source suggestions', () => { }); it('adds a metric column on a number field if no other metrics set', () => { + (generateId as jest.Mock).mockReturnValue('newid'); const initialState = stateWithNonEmptyTables(); const modifiedState: IndexPatternPrivateState = { ...initialState, @@ -955,10 +974,10 @@ describe('IndexPattern Data Source suggestions', () => { layers: { previousLayer: modifiedState.layers.previousLayer, currentLayer: expect.objectContaining({ - columnOrder: ['cola', 'id1'], + columnOrder: ['cola', 'newid'], columns: { ...modifiedState.layers.currentLayer.columns, - id1: expect.objectContaining({ + newid: expect.objectContaining({ operationType: 'avg', sourceField: 'memory', }), @@ -1008,6 +1027,137 @@ describe('IndexPattern Data Source suggestions', () => { const suggestions = getDatasourceSuggestionsForField(modifiedState, '1', documentField); expect(suggestions).not.toContain(expect.objectContaining({ changeType: 'extended' })); }); + + it('hides any referenced metrics when adding new metrics', () => { + (generateId as jest.Mock).mockReturnValue('newid'); + const initialState = stateWithNonEmptyTables(); + const modifiedState: IndexPatternPrivateState = { + ...initialState, + layers: { + currentLayer: { + indexPatternId: '1', + columnOrder: ['date', 'metric', 'ref'], + columns: { + date: { + label: '', + customLabel: true, + dataType: 'date', + isBucketed: true, + operationType: 'date_histogram', + sourceField: 'timestamp', + params: { interval: 'auto' }, + }, + metric: { + label: '', + customLabel: true, + dataType: 'number', + isBucketed: false, + operationType: 'avg', + sourceField: 'bytes', + }, + ref: { + label: '', + customLabel: true, + dataType: 'number', + isBucketed: false, + operationType: 'cumulative_sum', + references: ['metric'], + }, + }, + }, + }, + }; + const suggestions = getSuggestionSubset( + getDatasourceSuggestionsForField(modifiedState, '1', documentField) + ); + expect(suggestions).toContainEqual( + expect.objectContaining({ + table: expect.objectContaining({ + isMultiRow: true, + changeType: 'extended', + label: undefined, + layerId: 'currentLayer', + columns: [ + { + columnId: 'date', + operation: expect.objectContaining({ dataType: 'date', isBucketed: true }), + }, + { + columnId: 'newid', + operation: expect.objectContaining({ dataType: 'number', isBucketed: false }), + }, + { + columnId: 'ref', + operation: expect.objectContaining({ dataType: 'number', isBucketed: false }), + }, + ], + }), + keptLayerIds: ['currentLayer'], + }) + ); + }); + + it('makes a suggestion to extending from an invalid state with a new metric', () => { + (generateId as jest.Mock).mockReturnValue('newid'); + const initialState = stateWithNonEmptyTables(); + const modifiedState: IndexPatternPrivateState = { + ...initialState, + layers: { + currentLayer: { + indexPatternId: '1', + columnOrder: ['metric', 'ref'], + columns: { + metric: { + label: '', + customLabel: true, + dataType: 'number', + isBucketed: false, + operationType: 'avg', + sourceField: 'bytes', + }, + ref: { + label: '', + customLabel: true, + dataType: 'number', + isBucketed: false, + operationType: 'cumulative_sum', + references: ['metric'], + }, + }, + }, + }, + }; + const suggestions = getSuggestionSubset( + getDatasourceSuggestionsForField(modifiedState, '1', documentField) + ); + expect(suggestions).toContainEqual( + expect.objectContaining({ + table: expect.objectContaining({ + changeType: 'extended', + columns: [ + { + columnId: 'newid', + operation: { + dataType: 'number', + isBucketed: false, + label: 'Count of records', + scale: 'ratio', + }, + }, + { + columnId: 'ref', + operation: { + dataType: 'number', + isBucketed: false, + label: '', + scale: undefined, + }, + }, + ], + }), + }) + ); + }); }); describe('finding the layer that is using the current index pattern', () => { @@ -1121,6 +1271,7 @@ describe('IndexPattern Data Source suggestions', () => { }); }); }); + describe('#getDatasourceSuggestionsForVisualizeField', () => { describe('with no layer', () => { function stateWithoutLayer() { @@ -1218,6 +1369,7 @@ describe('IndexPattern Data Source suggestions', () => { columns: { cola: { label: 'My Op 2', + customLabel: true, dataType: 'string', isBucketed: true, @@ -1305,6 +1457,7 @@ describe('IndexPattern Data Source suggestions', () => { columns: { cola: { label: 'My Op', + customLabel: true, dataType: 'number', isBucketed: false, operationType: 'avg', @@ -1316,7 +1469,7 @@ describe('IndexPattern Data Source suggestions', () => { }, }; - expect(getDatasourceSuggestionsFromCurrentState(state)).toContainEqual( + expect(getSuggestionSubset(getDatasourceSuggestionsFromCurrentState(state))).toContainEqual( expect.objectContaining({ table: { isMultiRow: true, @@ -1359,6 +1512,7 @@ describe('IndexPattern Data Source suggestions', () => { columns: { cola: { label: 'My Terms', + customLabel: true, dataType: 'string', isBucketed: true, operationType: 'terms', @@ -1372,6 +1526,7 @@ describe('IndexPattern Data Source suggestions', () => { }, colb: { label: 'My Op', + customLabel: true, dataType: 'number', isBucketed: false, operationType: 'avg', @@ -1383,7 +1538,7 @@ describe('IndexPattern Data Source suggestions', () => { }, }; - expect(getDatasourceSuggestionsFromCurrentState(state)).toContainEqual( + expect(getSuggestionSubset(getDatasourceSuggestionsFromCurrentState(state))).toContainEqual( expect.objectContaining({ table: { isMultiRow: true, @@ -1442,6 +1597,7 @@ describe('IndexPattern Data Source suggestions', () => { }, colb: { label: 'My Op', + customLabel: true, dataType: 'number', isBucketed: true, operationType: 'range', @@ -1487,6 +1643,7 @@ describe('IndexPattern Data Source suggestions', () => { }, colb: { label: 'My Custom Range', + customLabel: true, dataType: 'string', isBucketed: true, operationType: 'range', @@ -1503,7 +1660,7 @@ describe('IndexPattern Data Source suggestions', () => { }, }; - expect(getDatasourceSuggestionsFromCurrentState(state)).toContainEqual( + expect(getSuggestionSubset(getDatasourceSuggestionsFromCurrentState(state))).toContainEqual( expect.objectContaining({ table: { changeType: 'extended', @@ -1555,6 +1712,7 @@ describe('IndexPattern Data Source suggestions', () => { columns: { id1: { label: 'My Op', + customLabel: true, dataType: 'number', isBucketed: false, operationType: 'avg', @@ -1631,6 +1789,7 @@ describe('IndexPattern Data Source suggestions', () => { columns: { col1: { label: 'My Op', + customLabel: true, dataType: 'string', isBucketed: true, @@ -1644,6 +1803,7 @@ describe('IndexPattern Data Source suggestions', () => { }, col2: { label: 'My Op', + customLabel: true, dataType: 'string', isBucketed: true, @@ -1657,6 +1817,7 @@ describe('IndexPattern Data Source suggestions', () => { }, col3: { label: 'My Op', + customLabel: true, dataType: 'string', isBucketed: true, @@ -1670,6 +1831,7 @@ describe('IndexPattern Data Source suggestions', () => { }, col4: { label: 'My Op', + customLabel: true, dataType: 'number', isBucketed: false, @@ -1678,6 +1840,7 @@ describe('IndexPattern Data Source suggestions', () => { }, col5: { label: 'My Op', + customLabel: true, dataType: 'number', isBucketed: false, @@ -1691,34 +1854,29 @@ describe('IndexPattern Data Source suggestions', () => { }; const suggestions = getDatasourceSuggestionsFromCurrentState(state); - // 1 bucket col, 2 metric cols - isTableWithBucketColumns(suggestions[0], ['col1', 'col4', 'col5'], 1); + + // 3 bucket cols, 2 metric cols + isTableWithBucketColumns(suggestions[0], ['col1', 'col2', 'col3', 'col4', 'col5'], 3); // 1 bucket col, 1 metric col isTableWithBucketColumns(suggestions[1], ['col1', 'col4'], 1); // 2 bucket cols, 2 metric cols - isTableWithBucketColumns(suggestions[2], ['col1', 'col2', 'col4', 'col5'], 2); - - // 2 bucket cols, 1 metric col - isTableWithBucketColumns(suggestions[3], ['col1', 'col2', 'col4'], 2); - - // 3 bucket cols, 2 metric cols - isTableWithBucketColumns(suggestions[4], ['col1', 'col2', 'col3', 'col4', 'col5'], 3); + isTableWithBucketColumns(suggestions[2], ['col1', 'col2', 'col4'], 2); // 3 bucket cols, 1 metric col - isTableWithBucketColumns(suggestions[5], ['col1', 'col2', 'col3', 'col4'], 3); + isTableWithBucketColumns(suggestions[3], ['col1', 'col2', 'col3', 'col4'], 3); // first metric col - isTableWithMetricColumns(suggestions[6], ['col4']); + isTableWithMetricColumns(suggestions[4], ['col4']); // second metric col - isTableWithMetricColumns(suggestions[7], ['col5']); + isTableWithMetricColumns(suggestions[5], ['col5']); - expect(suggestions.length).toBe(8); + expect(suggestions.length).toBe(6); }); - it('returns an only metric version of a given table', () => { + it('returns an only metric version of a given table, but does not include current state as reduced', () => { const initialState = testInitialState(); const state: IndexPatternPrivateState = { indexPatternRefs: [], @@ -1770,7 +1928,7 @@ describe('IndexPattern Data Source suggestions', () => { ...initialState.layers.first, columns: { id1: { - label: 'Date histogram', + label: 'field2', dataType: 'date', isBucketed: true, @@ -1794,8 +1952,34 @@ describe('IndexPattern Data Source suggestions', () => { }, }; - const suggestions = getDatasourceSuggestionsFromCurrentState(state); - expect(suggestions[1].table.columns[0].operation.label).toBe('Average of field1'); + const suggestions = getSuggestionSubset(getDatasourceSuggestionsFromCurrentState(state)); + expect(suggestions).not.toContainEqual( + expect.objectContaining({ + table: expect.objectContaining({ + changeType: 'reduced', + columns: [ + expect.objectContaining({ + operation: expect.objectContaining({ label: 'field2' }), + }), + expect.objectContaining({ + operation: expect.objectContaining({ label: 'Average of field1' }), + }), + ], + }), + }) + ); + expect(suggestions).toContainEqual( + expect.objectContaining({ + table: expect.objectContaining({ + changeType: 'reduced', + columns: [ + expect.objectContaining({ + operation: expect.objectContaining({ label: 'Average of field1' }), + }), + ], + }), + }) + ); }); it('returns an alternative metric for an only-metric table', () => { @@ -1848,9 +2032,18 @@ describe('IndexPattern Data Source suggestions', () => { }, }; - const suggestions = getDatasourceSuggestionsFromCurrentState(state); - expect(suggestions[0].table.columns.length).toBe(1); - expect(suggestions[0].table.columns[0].operation.label).toBe('Sum of field1'); + const suggestions = getSuggestionSubset(getDatasourceSuggestionsFromCurrentState(state)); + expect(suggestions).toContainEqual( + expect.objectContaining({ + table: expect.objectContaining({ + columns: [ + expect.objectContaining({ + operation: expect.objectContaining({ label: 'Sum of field1' }), + }), + ], + }), + }) + ); }); it('contains a reordering suggestion when there are exactly 2 buckets', () => { @@ -1909,7 +2102,7 @@ describe('IndexPattern Data Source suggestions', () => { ); }); - it('does not generate suggestions if invalid fields are referenced', () => { + it('will generate suggestions even if there are errors from missing fields', () => { const initialState = testInitialState(); const state: IndexPatternPrivateState = { indexPatternRefs: [], @@ -1937,8 +2130,259 @@ describe('IndexPattern Data Source suggestions', () => { }, }; - const suggestions = getDatasourceSuggestionsFromCurrentState(state); - expect(suggestions).toEqual([]); + const suggestions = getSuggestionSubset(getDatasourceSuggestionsFromCurrentState(state)); + expect(suggestions).toContainEqual( + expect.objectContaining({ + table: { + changeType: 'unchanged', + columns: [ + { + columnId: 'col1', + operation: { + dataType: 'string', + isBucketed: true, + label: 'My Op', + scale: undefined, + }, + }, + { + columnId: 'col2', + operation: { + dataType: 'string', + isBucketed: true, + label: 'Top 5', + scale: undefined, + }, + }, + ], + isMultiRow: true, + label: undefined, + layerId: 'first', + }, + }) + ); + }); + + describe('references', () => { + it('will extend the table with a date when starting in an invalid state', () => { + const initialState = testInitialState(); + const state: IndexPatternPrivateState = { + ...initialState, + layers: { + ...initialState.layers, + first: { + ...initialState.layers.first, + columnOrder: ['metric', 'ref', 'ref2'], + columns: { + metric: { + label: '', + dataType: 'number', + isBucketed: false, + operationType: 'count', + sourceField: 'Records', + }, + ref: { + label: '', + dataType: 'number', + isBucketed: false, + operationType: 'cumulative_sum', + references: ['metric'], + }, + ref2: { + label: '', + dataType: 'number', + isBucketed: false, + operationType: 'cumulative_sum', + references: ['metric2'], + }, + }, + }, + }, + }; + + const result = getSuggestionSubset(getDatasourceSuggestionsFromCurrentState(state)); + + expect(result).toContainEqual( + expect.objectContaining({ + table: expect.objectContaining({ + changeType: 'extended', + layerId: 'first', + columns: [ + { + columnId: 'id1', + operation: { + dataType: 'date', + isBucketed: true, + label: 'timestampLabel', + scale: 'interval', + }, + }, + { + columnId: 'ref', + operation: { + dataType: 'number', + isBucketed: false, + label: 'Cumulative sum of Records', + scale: undefined, + }, + }, + { + columnId: 'ref2', + operation: { + dataType: 'number', + isBucketed: false, + label: 'Cumulative sum of (incomplete)', + scale: undefined, + }, + }, + ], + }), + keptLayerIds: ['first'], + }) + ); + }); + + it('will make an unchanged suggestion including incomplete references', () => { + const initialState = testInitialState(); + const state: IndexPatternPrivateState = { + ...initialState, + layers: { + ...initialState.layers, + first: { + ...initialState.layers.first, + columnOrder: ['date', 'ref', 'ref2'], + columns: { + date: { + label: '', + dataType: 'date', + isBucketed: true, + operationType: 'date_histogram', + sourceField: 'timestamp', + params: { interval: 'auto' }, + }, + ref: { + label: '', + dataType: 'number', + isBucketed: false, + operationType: 'cumulative_sum', + references: ['metric'], + }, + ref2: { + label: '', + dataType: 'number', + isBucketed: false, + operationType: 'cumulative_sum', + references: ['metric'], + }, + }, + }, + }, + }; + + const result = getSuggestionSubset(getDatasourceSuggestionsFromCurrentState(state)); + + expect(result).toContainEqual( + expect.objectContaining({ + table: expect.objectContaining({ + changeType: 'unchanged', + layerId: 'first', + columns: [ + { + columnId: 'date', + operation: { + dataType: 'date', + isBucketed: true, + label: '', + scale: undefined, + }, + }, + { + columnId: 'ref', + operation: { + dataType: 'number', + isBucketed: false, + label: '', + scale: undefined, + }, + }, + { + columnId: 'ref2', + operation: { + dataType: 'number', + isBucketed: false, + label: '', + scale: undefined, + }, + }, + ], + }), + keptLayerIds: ['first'], + }) + ); + }); + + it('will skip a reduced suggestion when handling multiple references', () => { + const initialState = testInitialState(); + const state: IndexPatternPrivateState = { + ...initialState, + layers: { + ...initialState.layers, + first: { + ...initialState.layers.first, + columnOrder: ['date', 'metric', 'metric2', 'ref', 'ref2'], + + columns: { + date: { + label: '', + dataType: 'date', + isBucketed: true, + operationType: 'date_histogram', + sourceField: 'timestamp', + params: { interval: 'auto' }, + }, + metric: { + label: '', + dataType: 'number', + isBucketed: false, + operationType: 'count', + sourceField: 'Records', + }, + ref: { + label: '', + dataType: 'number', + isBucketed: false, + operationType: 'cumulative_sum', + references: ['metric'], + }, + metric2: { + label: '', + dataType: 'number', + isBucketed: false, + operationType: 'count', + sourceField: 'Records', + }, + ref2: { + label: '', + dataType: 'number', + isBucketed: false, + operationType: 'cumulative_sum', + references: ['metric2'], + }, + }, + }, + }, + }; + + const result = getSuggestionSubset(getDatasourceSuggestionsFromCurrentState(state)); + + expect(result).not.toContainEqual( + expect.objectContaining({ + table: expect.objectContaining({ + changeType: 'reduced', + }), + }) + ); + }); }); }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts index ebac396210a5c..9d7328b4dca37 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import _, { partition } from 'lodash'; +import _ from 'lodash'; import { i18n } from '@kbn/i18n'; import { generateId } from '../id_generator'; import { DatasourceSuggestion, TableChangeType } from '../types'; @@ -17,8 +17,10 @@ import { operationDefinitionMap, IndexPatternColumn, OperationType, + getExistingColumnGroups, + isReferenced, } from './operations'; -import { hasField, hasInvalidColumns } from './utils'; +import { hasField } from './utils'; import { IndexPattern, IndexPatternPrivateState, @@ -27,7 +29,7 @@ import { } from './types'; import { documentField } from './document_field'; -type IndexPatternSugestion = DatasourceSuggestion; +export type IndexPatternSuggestion = DatasourceSuggestion; function buildSuggestion({ state, @@ -71,10 +73,13 @@ function buildSuggestion({ }, table: { - columns: columnOrder.map((columnId) => ({ - columnId, - operation: columnToOperation(columnMap[columnId]), - })), + columns: columnOrder + // Hide any referenced columns from what visualizations know about + .filter((columnId) => !isReferenced(layers[layerId]!, columnId)) + .map((columnId) => ({ + columnId, + operation: columnToOperation(columnMap[columnId]), + })), isMultiRow, layerId, changeType, @@ -89,8 +94,7 @@ export function getDatasourceSuggestionsForField( state: IndexPatternPrivateState, indexPatternId: string, field: IndexPatternField -): IndexPatternSugestion[] { - if (hasInvalidColumns(state)) return []; +): IndexPatternSuggestion[] { const layers = Object.keys(state.layers); const layerIds = layers.filter((id) => state.layers[id].indexPatternId === indexPatternId); @@ -123,7 +127,7 @@ export function getDatasourceSuggestionsForVisualizeField( state: IndexPatternPrivateState, indexPatternId: string, fieldName: string -): IndexPatternSugestion[] { +): IndexPatternSuggestion[] { const layers = Object.keys(state.layers); const layerIds = layers.filter((id) => state.layers[id].indexPatternId === indexPatternId); // Identify the field by the indexPatternId and the fieldName @@ -158,7 +162,7 @@ function getExistingLayerSuggestionsForField( const fieldInUse = Object.values(layer.columns).some( (column) => hasField(column) && column.sourceField === field.name ); - const suggestions: IndexPatternSugestion[] = []; + const suggestions: IndexPatternSuggestion[] = []; if (usableAsBucketOperation && !fieldInUse) { if ( @@ -221,8 +225,9 @@ function getExistingLayerSuggestionsForField( ); } - const [, metrics] = separateBucketColumns(layer); - if (metrics.length === 1) { + const [, metrics, references] = getExistingColumnGroups(layer); + // TODO: Write test for the case where we have exactly one metric and one reference. We shouldn't switch the inner metric. + if (metrics.length === 1 && references.length === 0) { const layerWithReplacedMetric = replaceColumn({ layer, indexPattern, @@ -257,7 +262,7 @@ function getEmptyLayerSuggestionsForField( layerId: string, indexPatternId: string, field: IndexPatternField -): IndexPatternSugestion[] { +): IndexPatternSuggestion[] { const indexPattern = state.indexPatterns[indexPatternId]; let newLayer: IndexPatternLayer | undefined; const bucketOperation = getBucketOperation(field); @@ -331,7 +336,6 @@ function createNewLayerWithMetricAggregation( export function getDatasourceSuggestionsFromCurrentState( state: IndexPatternPrivateState ): Array> { - if (hasInvalidColumns(state)) return []; const layers = Object.entries(state.layers || {}); if (layers.length > 1) { // Return suggestions that reduce the data to each layer individually @@ -372,12 +376,13 @@ export function getDatasourceSuggestionsFromCurrentState( }), ]); } + return _.flatten( Object.entries(state.layers || {}) .filter(([_id, layer]) => layer.columnOrder.length && layer.indexPatternId) .map(([layerId, layer]) => { const indexPattern = state.indexPatterns[layer.indexPatternId]; - const [buckets, metrics] = separateBucketColumns(layer); + const [buckets, metrics, references] = getExistingColumnGroups(layer); const timeDimension = layer.columnOrder.find( (columnId) => layer.columns[columnId].isBucketed && layer.columns[columnId].dataType === 'date' @@ -390,29 +395,22 @@ export function getDatasourceSuggestionsFromCurrentState( buckets.some((columnId) => layer.columns[columnId].dataType === 'number'); const suggestions: Array> = []; - if (metrics.length === 0) { - // intermediary chart without metric, don't try to suggest reduced versions - suggestions.push( - buildSuggestion({ - state, - layerId, - changeType: 'unchanged', - }) - ); - } else if (buckets.length === 0) { + + // Always suggest an unchanged table, including during invalid states + suggestions.push( + buildSuggestion({ + state, + layerId, + changeType: 'unchanged', + }) + ); + + if (!references.length && metrics.length && buckets.length === 0) { if (timeField) { // suggest current metric over time if there is a default time field suggestions.push(createSuggestionWithDefaultDateHistogram(state, layerId, timeField)); } suggestions.push(...createAlternativeMetricSuggestions(indexPattern, layerId, state)); - // also suggest simple current state - suggestions.push( - buildSuggestion({ - state, - layerId, - changeType: 'unchanged', - }) - ); } else { suggestions.push(...createSimplifiedTableSuggestions(state, layerId)); @@ -570,7 +568,11 @@ function createSuggestionWithDefaultDateHistogram( function createSimplifiedTableSuggestions(state: IndexPatternPrivateState, layerId: string) { const layer = state.layers[layerId]; - const [availableBucketedColumns, availableMetricColumns] = separateBucketColumns(layer); + const [ + availableBucketedColumns, + availableMetricColumns, + availableReferenceColumns, + ] = getExistingColumnGroups(layer); return _.flatten( availableBucketedColumns.map((_col, index) => { @@ -581,29 +583,30 @@ function createSimplifiedTableSuggestions(state: IndexPatternPrivateState, layer columnOrder: [...bucketedColumns, ...availableMetricColumns], }; - if (availableMetricColumns.length > 1) { - return [ - allMetricsSuggestion, - { ...layer, columnOrder: [...bucketedColumns, availableMetricColumns[0]] }, - ]; + if (availableBucketedColumns.length <= 1 || availableReferenceColumns.length) { + // Don't simplify when dealing with single-bucket table. Also don't break + // reference-based columns by removing buckets. + return []; + } else if (availableMetricColumns.length > 1) { + return [{ ...layer, columnOrder: [...bucketedColumns, availableMetricColumns[0]] }]; } else { return allMetricsSuggestion; } }) ) .concat( - availableMetricColumns.map((columnId) => { - // build suggestions with only metrics - return { ...layer, columnOrder: [columnId] }; - }) + availableReferenceColumns.length + ? [] + : availableMetricColumns.map((columnId) => { + return { ...layer, columnOrder: [columnId] }; + }) ) .map((updatedLayer) => { return buildSuggestion({ state, layerId, updatedLayer, - changeType: - layer.columnOrder.length === updatedLayer.columnOrder.length ? 'unchanged' : 'reduced', + changeType: 'reduced', label: updatedLayer.columnOrder.length === 1 ? getMetricSuggestionTitle(updatedLayer, availableMetricColumns.length === 1) @@ -623,7 +626,3 @@ function getMetricSuggestionTitle(layer: IndexPatternLayer, onlyMetric: boolean) 'Title of a suggested chart containing only a single numerical metric calculated over all available data', }); } - -function separateBucketColumns(layer: IndexPatternLayer) { - return partition(layer.columnOrder, (columnId) => layer.columns[columnId].isBucketed); -} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/__mocks__/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/__mocks__/index.ts index ff900134df9a1..6d7a0117a1770 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/__mocks__/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/__mocks__/index.ts @@ -42,6 +42,7 @@ export const { getErrorMessages, isReferenced, resetIncomplete, + isOperationAllowedAsReference, } = actualHelpers; export const { adjustTimeScaleLabelSuffix, DEFAULT_TIME_SCALE } = actualTimeScaleUtils; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/counter_rate.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/counter_rate.tsx index 0cfba4cfc739f..4fd045c17740d 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/counter_rate.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/counter_rate.tsx @@ -9,6 +9,7 @@ import { FormattedIndexPatternColumn, ReferenceBasedIndexPatternColumn } from '. import { IndexPatternLayer } from '../../../types'; import { buildLabelFunction, + getErrorsForDateReference, checkForDateHistogram, dateBasedOperationToExpression, hasDateField, @@ -52,15 +53,18 @@ export const counterRateOperation: OperationDefinition< validateMetadata: (meta) => meta.dataType === 'number' && !meta.isBucketed, }, ], - getPossibleOperation: () => { - return { - dataType: 'number', - isBucketed: false, - scale: 'ratio', - }; + getPossibleOperation: (indexPattern) => { + if (hasDateField(indexPattern)) { + return { + dataType: 'number', + isBucketed: false, + scale: 'ratio', + }; + } }, getDefaultLabel: (column, indexPattern, columns) => { - return ofName(columns[column.references[0]]?.label, column.timeScale); + const ref = columns[column.references[0]]; + return ofName(ref && 'sourceField' in ref ? ref.sourceField : undefined, column.timeScale); }, toExpression: (layer, columnId) => { return dateBasedOperationToExpression(layer, columnId, 'lens_counter_rate'); @@ -69,7 +73,7 @@ export const counterRateOperation: OperationDefinition< const metric = layer.columns[referenceIds[0]]; const timeScale = previousColumn?.timeScale || DEFAULT_TIME_SCALE; return { - label: ofName(metric?.label, timeScale), + label: ofName(metric && 'sourceField' in metric ? metric.sourceField : undefined, timeScale), dataType: 'number', operationType: 'counter_rate', isBucketed: false, @@ -88,13 +92,22 @@ export const counterRateOperation: OperationDefinition< isTransferable: (column, newIndexPattern) => { return hasDateField(newIndexPattern); }, - getErrorMessage: (layer: IndexPatternLayer) => { - return checkForDateHistogram( + getErrorMessage: (layer: IndexPatternLayer, columnId: string) => { + return getErrorsForDateReference( layer, + columnId, i18n.translate('xpack.lens.indexPattern.counterRate', { defaultMessage: 'Counter rate', }) ); }, + getDisabledStatus(indexPattern, layer) { + return checkForDateHistogram( + layer, + i18n.translate('xpack.lens.indexPattern.counterRate', { + defaultMessage: 'Counter rate', + }) + )?.join(', '); + }, timeScalingMode: 'mandatory', }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/cumulative_sum.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/cumulative_sum.tsx index 9244aaaf90ab7..7067b6470bec7 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/cumulative_sum.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/cumulative_sum.tsx @@ -7,12 +7,17 @@ import { i18n } from '@kbn/i18n'; import { FormattedIndexPatternColumn, ReferenceBasedIndexPatternColumn } from '../column_types'; import { IndexPatternLayer } from '../../../types'; -import { checkForDateHistogram, dateBasedOperationToExpression } from './utils'; +import { + checkForDateHistogram, + getErrorsForDateReference, + dateBasedOperationToExpression, + hasDateField, +} from './utils'; import { OperationDefinition } from '..'; const ofName = (name?: string) => { return i18n.translate('xpack.lens.indexPattern.cumulativeSumOf', { - defaultMessage: 'Cumulative sum rate of {name}', + defaultMessage: 'Cumulative sum of {name}', values: { name: name ?? @@ -46,23 +51,26 @@ export const cumulativeSumOperation: OperationDefinition< validateMetadata: (meta) => meta.dataType === 'number' && !meta.isBucketed, }, ], - getPossibleOperation: () => { - return { - dataType: 'number', - isBucketed: false, - scale: 'ratio', - }; + getPossibleOperation: (indexPattern) => { + if (hasDateField(indexPattern)) { + return { + dataType: 'number', + isBucketed: false, + scale: 'ratio', + }; + } }, getDefaultLabel: (column, indexPattern, columns) => { - return ofName(columns[column.references[0]]?.label); + const ref = columns[column.references[0]]; + return ofName(ref && 'sourceField' in ref ? ref.sourceField : undefined); }, toExpression: (layer, columnId) => { return dateBasedOperationToExpression(layer, columnId, 'cumulative_sum'); }, buildColumn: ({ referenceIds, previousColumn, layer }) => { - const metric = layer.columns[referenceIds[0]]; + const ref = layer.columns[referenceIds[0]]; return { - label: ofName(metric?.label), + label: ofName(ref && 'sourceField' in ref ? ref.sourceField : undefined), dataType: 'number', operationType: 'cumulative_sum', isBucketed: false, @@ -80,12 +88,21 @@ export const cumulativeSumOperation: OperationDefinition< isTransferable: () => { return true; }, - getErrorMessage: (layer: IndexPatternLayer) => { - return checkForDateHistogram( + getErrorMessage: (layer: IndexPatternLayer, columnId: string) => { + return getErrorsForDateReference( layer, + columnId, i18n.translate('xpack.lens.indexPattern.cumulativeSum', { defaultMessage: 'Cumulative sum', }) ); }, + getDisabledStatus(indexPattern, layer) { + return checkForDateHistogram( + layer, + i18n.translate('xpack.lens.indexPattern.cumulativeSum', { + defaultMessage: 'Cumulative sum', + }) + )?.join(', '); + }, }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/derivative.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/derivative.tsx index 41fe361c7ba9c..358046ad5bfb9 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/derivative.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/derivative.tsx @@ -10,6 +10,7 @@ import { IndexPatternLayer } from '../../../types'; import { buildLabelFunction, checkForDateHistogram, + getErrorsForDateReference, dateBasedOperationToExpression, hasDateField, } from './utils'; @@ -51,23 +52,29 @@ export const derivativeOperation: OperationDefinition< validateMetadata: (meta) => meta.dataType === 'number' && !meta.isBucketed, }, ], - getPossibleOperation: () => { - return { - dataType: 'number', - isBucketed: false, - scale: 'ratio', - }; + getPossibleOperation: (indexPattern) => { + if (hasDateField(indexPattern)) { + return { + dataType: 'number', + isBucketed: false, + scale: 'ratio', + }; + } }, getDefaultLabel: (column, indexPattern, columns) => { - return ofName(columns[column.references[0]]?.label, column.timeScale); + const ref = columns[column.references[0]]; + return ofName(ref && 'sourceField' in ref ? ref.sourceField : undefined, column.timeScale); }, toExpression: (layer, columnId) => { return dateBasedOperationToExpression(layer, columnId, 'derivative'); }, buildColumn: ({ referenceIds, previousColumn, layer }) => { - const metric = layer.columns[referenceIds[0]]; + const ref = layer.columns[referenceIds[0]]; return { - label: ofName(metric?.label, previousColumn?.timeScale), + label: ofName( + ref && 'sourceField' in ref ? ref.sourceField : undefined, + previousColumn?.timeScale + ), dataType: 'number', operationType: 'derivative', isBucketed: false, @@ -87,13 +94,22 @@ export const derivativeOperation: OperationDefinition< return hasDateField(newIndexPattern); }, onOtherColumnChanged: adjustTimeScaleOnOtherColumnChange, - getErrorMessage: (layer: IndexPatternLayer) => { - return checkForDateHistogram( + getErrorMessage: (layer: IndexPatternLayer, columnId: string) => { + return getErrorsForDateReference( layer, + columnId, i18n.translate('xpack.lens.indexPattern.derivative', { defaultMessage: 'Differences', }) ); }, + getDisabledStatus(indexPattern, layer) { + return checkForDateHistogram( + layer, + i18n.translate('xpack.lens.indexPattern.derivative', { + defaultMessage: 'Differences', + }) + )?.join(', '); + }, timeScalingMode: 'optional', }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/moving_average.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/moving_average.tsx index 59d5924b9a370..d9805b337c000 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/moving_average.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/moving_average.tsx @@ -14,11 +14,12 @@ import { IndexPatternLayer } from '../../../types'; import { buildLabelFunction, checkForDateHistogram, + getErrorsForDateReference, dateBasedOperationToExpression, hasDateField, } from './utils'; import { updateColumnParam } from '../../layer_helpers'; -import { useDebounceWithOptions } from '../helpers'; +import { isValidNumber, useDebounceWithOptions } from '../helpers'; import { adjustTimeScaleOnOtherColumnChange } from '../../time_scale_utils'; import type { OperationDefinition, ParamEditorProps } from '..'; @@ -50,7 +51,7 @@ export const movingAverageOperation: OperationDefinition< type: 'moving_average', priority: 1, displayName: i18n.translate('xpack.lens.indexPattern.movingAverage', { - defaultMessage: 'Moving Average', + defaultMessage: 'Moving average', }), input: 'fullReference', selectionStyle: 'full', @@ -60,12 +61,14 @@ export const movingAverageOperation: OperationDefinition< validateMetadata: (meta) => meta.dataType === 'number' && !meta.isBucketed, }, ], - getPossibleOperation: () => { - return { - dataType: 'number', - isBucketed: false, - scale: 'ratio', - }; + getPossibleOperation: (indexPattern) => { + if (hasDateField(indexPattern)) { + return { + dataType: 'number', + isBucketed: false, + scale: 'ratio', + }; + } }, getDefaultLabel: (column, indexPattern, columns) => { return ofName(columns[column.references[0]]?.label, column.timeScale); @@ -99,14 +102,23 @@ export const movingAverageOperation: OperationDefinition< return hasDateField(newIndexPattern); }, onOtherColumnChanged: adjustTimeScaleOnOtherColumnChange, - getErrorMessage: (layer: IndexPatternLayer) => { - return checkForDateHistogram( + getErrorMessage: (layer: IndexPatternLayer, columnId: string) => { + return getErrorsForDateReference( layer, + columnId, i18n.translate('xpack.lens.indexPattern.movingAverage', { - defaultMessage: 'Moving Average', + defaultMessage: 'Moving average', }) ); }, + getDisabledStatus(indexPattern, layer) { + return checkForDateHistogram( + layer, + i18n.translate('xpack.lens.indexPattern.movingAverage', { + defaultMessage: 'Moving average', + }) + )?.join(', '); + }, timeScalingMode: 'optional', }; @@ -120,10 +132,8 @@ function MovingAverageParamEditor({ useDebounceWithOptions( () => { - if (inputValue === '') { - return; - } - const inputNumber = Number(inputValue); + if (!isValidNumber(inputValue, true, undefined, 1)) return; + const inputNumber = parseInt(inputValue, 10); updateLayer( updateColumnParam({ layer, @@ -137,6 +147,7 @@ function MovingAverageParamEditor({ 256, [inputValue] ); + return ( ) => setInputValue(e.target.value)} + min={1} + step={1} + isInvalid={!isValidNumber(inputValue)} /> ); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.test.ts new file mode 100644 index 0000000000000..403f2b87ac86e --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.test.ts @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { checkReferences } from './utils'; +import { operationDefinitionMap } from '..'; +import { createMockedReferenceOperation } from '../../mocks'; + +// Mock prevents issue with circular loading +jest.mock('..'); + +describe('utils', () => { + beforeEach(() => { + // @ts-expect-error test-only operation type + operationDefinitionMap.testReference = createMockedReferenceOperation(); + }); + + describe('checkReferences', () => { + it('should show an error if the reference is missing', () => { + expect( + checkReferences( + { + columns: { + ref: { + label: 'Label', + // @ts-expect-error test-only operation type + operationType: 'testReference', + isBucketed: false, + dataType: 'number', + references: ['missing'], + }, + }, + columnOrder: ['ref'], + indexPatternId: '', + }, + 'ref' + ) + ).toEqual(['"Label" is not fully configured']); + }); + + it('should show an error if the reference is not allowed per the requirements', () => { + expect( + checkReferences( + { + columns: { + ref: { + label: 'Label', + // @ts-expect-error test-only operation type + operationType: 'testReference', + isBucketed: false, + dataType: 'number', + references: ['invalid'], + }, + invalid: { + label: 'Date', + operationType: 'date_histogram', + isBucketed: true, + dataType: 'date', + sourceField: 'timestamp', + params: { interval: 'auto' }, + }, + }, + columnOrder: ['invalid', 'ref'], + indexPatternId: '', + }, + 'ref' + ) + ).toEqual(['Dimension "Label" is configured incorrectly']); + }); + }); +}); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.ts index bac45f683e444..ca4b7c53b7ec7 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.ts @@ -5,11 +5,13 @@ */ import { i18n } from '@kbn/i18n'; -import { ExpressionFunctionAST } from '@kbn/interpreter/common'; -import { TimeScaleUnit } from '../../../time_scale'; -import { IndexPattern, IndexPatternLayer } from '../../../types'; +import type { ExpressionFunctionAST } from '@kbn/interpreter/common'; +import type { TimeScaleUnit } from '../../../time_scale'; +import type { IndexPattern, IndexPatternLayer } from '../../../types'; import { adjustTimeScaleLabelSuffix } from '../../time_scale_utils'; -import { ReferenceBasedIndexPatternColumn } from '../column_types'; +import type { ReferenceBasedIndexPatternColumn } from '../column_types'; +import { operationDefinitionMap } from '..'; +import type { IndexPatternColumn, RequiredReference } from '..'; export const buildLabelFunction = (ofName: (name?: string) => string) => ( name?: string, @@ -41,6 +43,78 @@ export function checkForDateHistogram(layer: IndexPatternLayer, name: string) { ]; } +export function checkReferences(layer: IndexPatternLayer, columnId: string) { + const column = layer.columns[columnId] as ReferenceBasedIndexPatternColumn; + + const errors: string[] = []; + + column.references.forEach((referenceId, index) => { + if (!layer.columns[referenceId]) { + errors.push( + i18n.translate('xpack.lens.indexPattern.missingReferenceError', { + defaultMessage: '"{dimensionLabel}" is not fully configured', + values: { + dimensionLabel: column.label, + }, + }) + ); + } else { + const referenceColumn = layer.columns[referenceId]!; + const definition = operationDefinitionMap[column.operationType]; + if (definition.input !== 'fullReference') { + throw new Error('inconsistent state - column is not a reference operation'); + } + const requirements = definition.requiredReferences[index]; + const isValid = isColumnValidAsReference({ + validation: requirements, + column: referenceColumn, + }); + + if (!isValid) { + errors.push( + i18n.translate('xpack.lens.indexPattern.invalidReferenceConfiguration', { + defaultMessage: 'Dimension "{dimensionLabel}" is configured incorrectly', + values: { + dimensionLabel: column.label, + }, + }) + ); + } + } + }); + return errors.length ? errors : undefined; +} + +export function isColumnValidAsReference({ + column, + validation, +}: { + column: IndexPatternColumn; + validation: RequiredReference; +}): boolean { + if (!column) return false; + const operationType = column.operationType; + const operationDefinition = operationDefinitionMap[operationType]; + return ( + validation.input.includes(operationDefinition.input) && + (!validation.specificOperations || validation.specificOperations.includes(operationType)) && + validation.validateMetadata(column) + ); +} + +export function getErrorsForDateReference( + layer: IndexPatternLayer, + columnId: string, + name: string +) { + const dateErrors = checkForDateHistogram(layer, name) ?? []; + const referenceErrors = checkReferences(layer, columnId) ?? []; + if (dateErrors.length || referenceErrors.length) { + return [...dateErrors, ...referenceErrors]; + } + return; +} + export function hasDateField(indexPattern: IndexPattern) { return indexPattern.fields.some((field) => field.type === 'date'); } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/cardinality.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/cardinality.tsx index 95e905f6021be..970f56020c7cd 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/cardinality.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/cardinality.tsx @@ -10,7 +10,7 @@ import { buildExpressionFunction } from '../../../../../../../src/plugins/expres import { OperationDefinition } from './index'; import { FormattedIndexPatternColumn, FieldBasedIndexPatternColumn } from './column_types'; -import { getInvalidFieldMessage } from './helpers'; +import { getInvalidFieldMessage, getSafeName } from './helpers'; const supportedTypes = new Set(['string', 'boolean', 'number', 'ip', 'date']); @@ -21,7 +21,9 @@ const IS_BUCKETED = false; function ofName(name: string) { return i18n.translate('xpack.lens.indexPattern.cardinalityOf', { defaultMessage: 'Unique count of {name}', - values: { name }, + values: { + name, + }, }); } @@ -58,8 +60,7 @@ export const cardinalityOperation: OperationDefinition - ofName(indexPattern.getFieldByName(column.sourceField)!.displayName), + getDefaultLabel: (column, indexPattern) => ofName(getSafeName(column.sourceField, indexPattern)), buildColumn({ field, previousColumn }) { return { label: ofName(field.displayName), diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/count.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/count.tsx index 0d8ed44f528a8..06d330a4a7eb2 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/count.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/count.tsx @@ -69,7 +69,12 @@ export const countOperation: OperationDefinition + adjustTimeScaleOnOtherColumnChange( + layer, + thisColumnId, + changedColumnId + ), toEsAggsFn: (column, columnId) => { return buildExpressionFunction('aggCount', { id: columnId, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.test.tsx index eadcf8384b1dd..abd033c0db4cf 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.test.tsx @@ -199,7 +199,8 @@ describe('date_histogram', () => { const esAggsFn = dateHistogramOperation.toEsAggsFn( layer.columns.col1 as DateHistogramIndexPatternColumn, 'col1', - indexPattern1 + indexPattern1, + layer ); expect(esAggsFn).toEqual( expect.objectContaining({ @@ -250,7 +251,8 @@ describe('date_histogram', () => { }, }, ]), - } + }, + layer ); expect(esAggsFn).toEqual( expect.objectContaining({ @@ -689,4 +691,32 @@ describe('date_histogram', () => { expect(instance.find('[data-test-subj="lensDateHistogramValue"]').exists()).toBeFalsy(); }); }); + + describe('getDefaultLabel', () => { + it('should not throw when the source field is not located', () => { + expect( + dateHistogramOperation.getDefaultLabel( + { + label: '', + dataType: 'date', + isBucketed: true, + operationType: 'date_histogram', + sourceField: 'missing', + params: { interval: 'auto' }, + }, + indexPattern1, + { + col1: { + label: '', + dataType: 'date', + isBucketed: true, + operationType: 'date_histogram', + sourceField: 'missing', + params: { interval: 'auto' }, + }, + } + ) + ).toEqual('Missing field'); + }); + }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx index cdd1ccad96a99..a41cc88c4f292 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx @@ -28,7 +28,7 @@ import { search, } from '../../../../../../../src/plugins/data/public'; import { buildExpressionFunction } from '../../../../../../../src/plugins/expressions/public'; -import { getInvalidFieldMessage } from './helpers'; +import { getInvalidFieldMessage, getSafeName } from './helpers'; const { isValidInterval } = search.aggs; const autoInterval = 'auto'; @@ -67,8 +67,7 @@ export const dateHistogramOperation: OperationDefinition< }; } }, - getDefaultLabel: (column, indexPattern) => - indexPattern.getFieldByName(column.sourceField)!.displayName, + getDefaultLabel: (column, indexPattern) => getSafeName(column.sourceField, indexPattern), buildColumn({ field }) { let interval = autoInterval; let timeZone: string | undefined; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.test.tsx index cf57c35f6f68b..86767fbc8b469 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.test.tsx @@ -83,7 +83,8 @@ describe('filters', () => { const esAggsFn = filtersOperation.toEsAggsFn( layer.columns.col1 as FiltersIndexPatternColumn, 'col1', - createMockedIndexPattern() + createMockedIndexPattern(), + layer ); expect(esAggsFn).toEqual( expect.objectContaining({ diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.test.ts new file mode 100644 index 0000000000000..04e04816d98ef --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.test.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { createMockedIndexPattern } from '../../mocks'; +import { getInvalidFieldMessage } from './helpers'; + +describe('helpers', () => { + describe('getInvalidFieldMessage', () => { + it('return an error if a field was removed', () => { + const messages = getInvalidFieldMessage( + { + dataType: 'number', + isBucketed: false, + label: 'Foo', + operationType: 'count', // <= invalid + sourceField: 'bytes', + }, + createMockedIndexPattern() + ); + expect(messages).toHaveLength(1); + expect(messages![0]).toEqual('Field bytes was not found'); + }); + + it('returns an error if a field is the wrong type', () => { + const messages = getInvalidFieldMessage( + { + dataType: 'number', + isBucketed: false, + label: 'Foo', + operationType: 'avg', // <= invalid + sourceField: 'timestamp', + }, + createMockedIndexPattern() + ); + expect(messages).toHaveLength(1); + expect(messages![0]).toEqual('Field timestamp was not found'); + }); + + it('returns no message if all fields are matching', () => { + const messages = getInvalidFieldMessage( + { + dataType: 'number', + isBucketed: false, + label: 'Foo', + operationType: 'avg', + sourceField: 'bytes', + }, + createMockedIndexPattern() + ); + expect(messages).toBeUndefined(); + }); + }); +}); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.tsx index 640a357d9a7a4..29148052cee8e 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.tsx @@ -7,7 +7,7 @@ import { useRef } from 'react'; import useDebounce from 'react-use/lib/useDebounce'; import { i18n } from '@kbn/i18n'; -import { operationDefinitionMap } from '.'; +import { IndexPatternColumn, operationDefinitionMap } from '.'; import { FieldBasedIndexPatternColumn } from './column_types'; import { IndexPattern } from '../../types'; @@ -62,3 +62,38 @@ export function getInvalidFieldMessage( ] : undefined; } + +export function getEsAggsSuffix(column: IndexPatternColumn) { + const operationDefinition = operationDefinitionMap[column.operationType]; + return operationDefinition.input === 'field' && operationDefinition.getEsAggsSuffix + ? operationDefinition.getEsAggsSuffix(column) + : ''; +} + +export function getSafeName(name: string, indexPattern: IndexPattern): string { + const field = indexPattern.getFieldByName(name); + return field + ? field.displayName + : i18n.translate('xpack.lens.indexPattern.missingFieldLabel', { + defaultMessage: 'Missing field', + }); +} + +export function isValidNumber( + inputValue: string | number | null | undefined, + integer?: boolean, + upperBound?: number, + lowerBound?: number +) { + const inputValueAsNumber = Number(inputValue); + return ( + inputValue !== '' && + inputValue !== null && + inputValue !== undefined && + !Number.isNaN(inputValueAsNumber) && + Number.isFinite(inputValueAsNumber) && + (!integer || Number.isInteger(inputValueAsNumber)) && + (upperBound === undefined || inputValueAsNumber <= upperBound) && + (lowerBound === undefined || inputValueAsNumber >= lowerBound) + ); +} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts index 6431dac7b381d..36c9cf75d2b6c 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts @@ -9,6 +9,7 @@ import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { termsOperation, TermsIndexPatternColumn } from './terms'; import { filtersOperation, FiltersIndexPatternColumn } from './filters'; import { cardinalityOperation, CardinalityIndexPatternColumn } from './cardinality'; +import { percentileOperation, PercentileIndexPatternColumn } from './percentile'; import { minOperation, MinIndexPatternColumn, @@ -58,6 +59,7 @@ export type IndexPatternColumn = | CardinalityIndexPatternColumn | SumIndexPatternColumn | MedianIndexPatternColumn + | PercentileIndexPatternColumn | CountIndexPatternColumn | LastValueIndexPatternColumn | CumulativeSumIndexPatternColumn @@ -82,6 +84,7 @@ const internalOperationDefinitions = [ cardinalityOperation, sumOperation, medianOperation, + percentileOperation, lastValueOperation, countOperation, rangeOperation, @@ -96,6 +99,7 @@ export { rangeOperation } from './ranges'; export { filtersOperation } from './filters'; export { dateHistogramOperation } from './date_histogram'; export { minOperation, averageOperation, sumOperation, maxOperation } from './metrics'; +export { percentileOperation } from './percentile'; export { countOperation } from './count'; export { lastValueOperation } from './last_value'; export { @@ -152,8 +156,9 @@ interface BaseOperationDefinitionProps { * return an updated column. If not implemented, the `id` function is used instead. */ onOtherColumnChanged?: ( - currentColumn: C, - columns: Partial> + layer: IndexPatternLayer, + thisColumnId: string, + changedColumnId: string ) => C; /** * React component for operation specific settings shown in the popover editor @@ -176,7 +181,7 @@ interface BaseOperationDefinitionProps { * but disable it from usage, this function returns the string describing * the status. Otherwise it returns undefined */ - getDisabledStatus?: (indexPattern: IndexPattern) => string | undefined; + getDisabledStatus?: (indexPattern: IndexPattern, layer: IndexPatternLayer) => string | undefined; /** * Validate that the operation has the right preconditions in the state. For example: * @@ -222,7 +227,12 @@ interface FieldlessOperationDefinition { * Function turning a column into an agg config passed to the `esaggs` function * together with the agg configs returned from other columns. */ - toEsAggsFn: (column: C, columnId: string, indexPattern: IndexPattern) => ExpressionAstFunction; + toEsAggsFn: ( + column: C, + columnId: string, + indexPattern: IndexPattern, + layer: IndexPatternLayer + ) => ExpressionAstFunction; } interface FieldBasedOperationDefinition { @@ -261,7 +271,19 @@ interface FieldBasedOperationDefinition { * Function turning a column into an agg config passed to the `esaggs` function * together with the agg configs returned from other columns. */ - toEsAggsFn: (column: C, columnId: string, indexPattern: IndexPattern) => ExpressionAstFunction; + toEsAggsFn: ( + column: C, + columnId: string, + indexPattern: IndexPattern, + layer: IndexPatternLayer + ) => ExpressionAstFunction; + /** + * Optional function to return the suffix used for ES bucket paths and esaggs column id. + * This is relevant for multi metrics to pick the right value. + * + * @param column The current column + */ + getEsAggsSuffix?: (column: C) => string; /** * Validate that the operation has the right preconditions in the state. For example: * @@ -314,9 +336,9 @@ interface FullReferenceOperationDefinition { ) => ReferenceBasedIndexPatternColumn & C; /** * Returns the meta data of the operation if applied. Undefined - * if the field is not applicable. + * if the operation can't be added with these fields. */ - getPossibleOperation: () => OperationMetadata; + getPossibleOperation: (indexPattern: IndexPattern) => OperationMetadata | undefined; /** * A chain of expression functions which will transform the table */ diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx index 817958aee5490..96b12a714e613 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx @@ -69,7 +69,8 @@ describe('last_value', () => { const esAggsFn = lastValueOperation.toEsAggsFn( { ...lastValueColumn, params: { ...lastValueColumn.params } }, 'col1', - {} as IndexPattern + {} as IndexPattern, + layer ); expect(esAggsFn).toEqual( expect.objectContaining({ @@ -311,13 +312,13 @@ describe('last_value', () => { it('should return disabledStatus if indexPattern does contain date field', () => { const indexPattern = createMockedIndexPattern(); - expect(lastValueOperation.getDisabledStatus!(indexPattern)).toEqual(undefined); + expect(lastValueOperation.getDisabledStatus!(indexPattern, layer)).toEqual(undefined); const indexPatternWithoutTimeFieldName = { ...indexPattern, timeFieldName: undefined, }; - expect(lastValueOperation.getDisabledStatus!(indexPatternWithoutTimeFieldName)).toEqual( + expect(lastValueOperation.getDisabledStatus!(indexPatternWithoutTimeFieldName, layer)).toEqual( undefined ); @@ -326,7 +327,10 @@ describe('last_value', () => { fields: indexPattern.fields.filter((f) => f.type !== 'date'), }; - const disabledStatus = lastValueOperation.getDisabledStatus!(indexPatternWithoutTimefields); + const disabledStatus = lastValueOperation.getDisabledStatus!( + indexPatternWithoutTimefields, + layer + ); expect(disabledStatus).toEqual( 'This function requires the presence of a date field in your index' ); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx index 7b5aee860654a..256ef7f75676d 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx @@ -13,12 +13,14 @@ import { FieldBasedIndexPatternColumn } from './column_types'; import { IndexPatternField, IndexPattern } from '../../types'; import { updateColumnParam } from '../layer_helpers'; import { DataType } from '../../../types'; -import { getInvalidFieldMessage } from './helpers'; +import { getInvalidFieldMessage, getSafeName } from './helpers'; function ofName(name: string) { return i18n.translate('xpack.lens.indexPattern.lastValueOf', { defaultMessage: 'Last value of {name}', - values: { name }, + values: { + name, + }, }); } @@ -87,8 +89,7 @@ export const lastValueOperation: OperationDefinition - indexPattern.getFieldByName(column.sourceField)!.displayName, + getDefaultLabel: (column, indexPattern) => ofName(getSafeName(column.sourceField, indexPattern)), input: 'field', onFieldChange: (oldColumn, field) => { const newParams = { ...oldColumn.params }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx index a886bfdaad325..470a5407b2589 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import { buildExpressionFunction } from '../../../../../../../src/plugins/expressions/public'; import { OperationDefinition } from './index'; -import { getInvalidFieldMessage } from './helpers'; +import { getInvalidFieldMessage, getSafeName } from './helpers'; import { FormattedIndexPatternColumn, FieldBasedIndexPatternColumn, @@ -45,11 +45,11 @@ function buildMetricOperation>({ optionalTimeScaling?: boolean; }) { const labelLookup = (name: string, column?: BaseIndexPatternColumn) => { - const rawLabel = ofName(name); + const label = ofName(name); if (!optionalTimeScaling) { - return rawLabel; + return label; } - return adjustTimeScaleLabelSuffix(rawLabel, undefined, column?.timeScale); + return adjustTimeScaleLabelSuffix(label, undefined, column?.timeScale); }; return { @@ -81,21 +81,26 @@ function buildMetricOperation>({ (!newField.aggregationRestrictions || newField.aggregationRestrictions![type]) ); }, - onOtherColumnChanged: (column, otherColumns) => - optionalTimeScaling ? adjustTimeScaleOnOtherColumnChange(column, otherColumns) : column, + onOtherColumnChanged: (layer, thisColumnId, changedColumnId) => + optionalTimeScaling + ? (adjustTimeScaleOnOtherColumnChange(layer, thisColumnId, changedColumnId) as T) + : (layer.columns[thisColumnId] as T), getDefaultLabel: (column, indexPattern, columns) => - labelLookup(indexPattern.getFieldByName(column.sourceField)!.displayName, column), - buildColumn: ({ field, previousColumn }) => ({ - label: labelLookup(field.displayName, previousColumn), - dataType: 'number', - operationType: type, - sourceField: field.name, - isBucketed: false, - scale: 'ratio', - timeScale: optionalTimeScaling ? previousColumn?.timeScale : undefined, - params: - previousColumn && previousColumn.dataType === 'number' ? previousColumn.params : undefined, - }), + labelLookup(getSafeName(column.sourceField, indexPattern), column), + buildColumn: ({ field, previousColumn }) => + ({ + label: labelLookup(field.displayName, previousColumn), + dataType: 'number', + operationType: type, + sourceField: field.name, + isBucketed: false, + scale: 'ratio', + timeScale: optionalTimeScaling ? previousColumn?.timeScale : undefined, + params: + previousColumn && previousColumn.dataType === 'number' + ? previousColumn.params + : undefined, + } as T), onFieldChange: (oldColumn, field) => { return { ...oldColumn, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.test.tsx new file mode 100644 index 0000000000000..c22eec62ea1ab --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.test.tsx @@ -0,0 +1,237 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { shallow, mount } from 'enzyme'; +import { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'kibana/public'; +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; +import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks'; +import { createMockedIndexPattern } from '../../mocks'; +import { percentileOperation } from './index'; +import { IndexPattern, IndexPatternLayer } from '../../types'; +import { PercentileIndexPatternColumn } from './percentile'; +import { EuiFieldNumber } from '@elastic/eui'; +import { act } from 'react-dom/test-utils'; +import { EuiFormRow } from '@elastic/eui'; + +const defaultProps = { + storage: {} as IStorageWrapper, + uiSettings: {} as IUiSettingsClient, + savedObjectsClient: {} as SavedObjectsClientContract, + dateRange: { fromDate: 'now-1d', toDate: 'now' }, + data: dataPluginMock.createStartContract(), + http: {} as HttpSetup, + indexPattern: { + ...createMockedIndexPattern(), + hasRestrictions: false, + } as IndexPattern, +}; + +describe('percentile', () => { + let layer: IndexPatternLayer; + const InlineOptions = percentileOperation.paramEditor!; + + beforeEach(() => { + layer = { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + col1: { + label: 'Top value of category', + dataType: 'string', + isBucketed: true, + operationType: 'terms', + params: { + orderBy: { type: 'alphabetical' }, + size: 3, + orderDirection: 'asc', + }, + sourceField: 'category', + }, + col2: { + label: '23rd percentile of a', + dataType: 'number', + isBucketed: false, + sourceField: 'a', + operationType: 'percentile', + params: { + percentile: 23, + }, + }, + }, + }; + }); + + describe('toEsAggsFn', () => { + it('should reflect params correctly', () => { + const percentileColumn = layer.columns.col2 as PercentileIndexPatternColumn; + const esAggsFn = percentileOperation.toEsAggsFn( + percentileColumn, + 'col1', + {} as IndexPattern, + layer + ); + expect(esAggsFn).toEqual( + expect.objectContaining({ + arguments: expect.objectContaining({ + percents: [23], + field: ['a'], + }), + }) + ); + }); + }); + + describe('onFieldChange', () => { + it('should change correctly to new field', () => { + const oldColumn: PercentileIndexPatternColumn = { + operationType: 'percentile', + sourceField: 'bytes', + label: '23rd percentile of bytes', + isBucketed: true, + dataType: 'number', + params: { + percentile: 23, + }, + }; + const indexPattern = createMockedIndexPattern(); + const newNumberField = indexPattern.getFieldByName('memory')!; + const column = percentileOperation.onFieldChange(oldColumn, newNumberField); + + expect(column).toEqual( + expect.objectContaining({ + dataType: 'number', + sourceField: 'memory', + params: expect.objectContaining({ + percentile: 23, + }), + }) + ); + expect(column.label).toContain('memory'); + }); + }); + + describe('buildColumn', () => { + it('should set default percentile', () => { + const indexPattern = createMockedIndexPattern(); + const bytesField = indexPattern.fields.find(({ name }) => name === 'bytes')!; + bytesField.displayName = 'test'; + const percentileColumn = percentileOperation.buildColumn({ + indexPattern, + field: bytesField, + layer: { columns: {}, columnOrder: [], indexPatternId: '' }, + }); + expect(percentileColumn.dataType).toEqual('number'); + expect(percentileColumn.params.percentile).toEqual(95); + expect(percentileColumn.label).toEqual('95th percentile of test'); + }); + }); + + describe('param editor', () => { + it('should render current percentile', () => { + const updateLayerSpy = jest.fn(); + const instance = shallow( + + ); + + const input = instance.find('[data-test-subj="lns-indexPattern-percentile-input"]'); + + expect(input.prop('value')).toEqual('23'); + }); + + it('should update state on change', async () => { + jest.useFakeTimers(); + const updateLayerSpy = jest.fn(); + const instance = mount( + + ); + + jest.runAllTimers(); + + const input = instance + .find('[data-test-subj="lns-indexPattern-percentile-input"]') + .find(EuiFieldNumber); + + await act(async () => { + input.prop('onChange')!({ target: { value: '27' } } as React.ChangeEvent); + }); + + instance.update(); + + jest.runAllTimers(); + + expect(updateLayerSpy).toHaveBeenCalledWith({ + ...layer, + columns: { + ...layer.columns, + col2: { + ...layer.columns.col2, + params: { + percentile: 27, + }, + label: '27th percentile of a', + }, + }, + }); + }); + + it('should not update on invalid input, but show invalid value locally', async () => { + const updateLayerSpy = jest.fn(); + const instance = mount( + + ); + + jest.runAllTimers(); + + const input = instance + .find('[data-test-subj="lns-indexPattern-percentile-input"]') + .find(EuiFieldNumber); + + await act(async () => { + input.prop('onChange')!({ + target: { value: '12.12' }, + } as React.ChangeEvent); + }); + + instance.update(); + + jest.runAllTimers(); + + expect(updateLayerSpy).not.toHaveBeenCalled(); + + expect( + instance + .find('[data-test-subj="lns-indexPattern-percentile-form"]') + .find(EuiFormRow) + .prop('isInvalid') + ).toEqual(true); + expect( + instance + .find('[data-test-subj="lns-indexPattern-percentile-input"]') + .find(EuiFieldNumber) + .prop('value') + ).toEqual('12.12'); + }); + }); +}); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.tsx new file mode 100644 index 0000000000000..b381a0ecb664a --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.tsx @@ -0,0 +1,189 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiFieldNumber, EuiFormRow } from '@elastic/eui'; +import React, { useCallback, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { AggFunctionsMapping } from 'src/plugins/data/public'; +import { buildExpressionFunction } from '../../../../../../../src/plugins/expressions/public'; +import { OperationDefinition } from './index'; +import { + getInvalidFieldMessage, + getSafeName, + isValidNumber, + useDebounceWithOptions, +} from './helpers'; +import { FieldBasedIndexPatternColumn } from './column_types'; + +export interface PercentileIndexPatternColumn extends FieldBasedIndexPatternColumn { + operationType: 'percentile'; + params: { + percentile: number; + format?: { + id: string; + params?: { + decimals: number; + }; + }; + }; +} + +function ofName(name: string, percentile: number) { + return i18n.translate('xpack.lens.indexPattern.percentileOf', { + defaultMessage: + '{percentile, selectordinal, one {#st} two {#nd} few {#rd} other {#th}} percentile of {name}', + values: { name, percentile }, + }); +} + +const DEFAULT_PERCENTILE_VALUE = 95; + +export const percentileOperation: OperationDefinition = { + type: 'percentile', + displayName: i18n.translate('xpack.lens.indexPattern.percentile', { + defaultMessage: 'Percentile', + }), + input: 'field', + getPossibleOperationForField: ({ aggregationRestrictions, aggregatable, type: fieldType }) => { + if (fieldType === 'number' && aggregatable && !aggregationRestrictions) { + return { + dataType: 'number', + isBucketed: false, + scale: 'ratio', + }; + } + }, + isTransferable: (column, newIndexPattern) => { + const newField = newIndexPattern.getFieldByName(column.sourceField); + + return Boolean( + newField && + newField.type === 'number' && + newField.aggregatable && + !newField.aggregationRestrictions + ); + }, + getDefaultLabel: (column, indexPattern, columns) => + ofName(getSafeName(column.sourceField, indexPattern), column.params.percentile), + buildColumn: ({ field, previousColumn, indexPattern }) => { + const existingFormat = + previousColumn?.params && 'format' in previousColumn?.params + ? previousColumn?.params?.format + : undefined; + const existingPercentileParam = + previousColumn?.operationType === 'percentile' && previousColumn?.params.percentile; + const newPercentileParam = existingPercentileParam || DEFAULT_PERCENTILE_VALUE; + return { + label: ofName(getSafeName(field.name, indexPattern), newPercentileParam), + dataType: 'number', + operationType: 'percentile', + sourceField: field.name, + isBucketed: false, + scale: 'ratio', + params: { + format: existingFormat, + percentile: newPercentileParam, + }, + }; + }, + onFieldChange: (oldColumn, field) => { + return { + ...oldColumn, + label: ofName(field.displayName, oldColumn.params.percentile), + sourceField: field.name, + }; + }, + toEsAggsFn: (column, columnId, _indexPattern) => { + return buildExpressionFunction('aggPercentiles', { + id: columnId, + enabled: true, + schema: 'metric', + field: column.sourceField, + percents: [column.params.percentile], + }).toAst(); + }, + getEsAggsSuffix: (column) => { + const value = column.params.percentile; + return `.${value}`; + }, + getErrorMessage: (layer, columnId, indexPattern) => + getInvalidFieldMessage(layer.columns[columnId] as FieldBasedIndexPatternColumn, indexPattern), + paramEditor: function PercentileParamEditor({ + layer, + updateLayer, + currentColumn, + columnId, + indexPattern, + }) { + const [inputValue, setInputValue] = useState(String(currentColumn.params.percentile)); + + const inputValueAsNumber = Number(inputValue); + // an input is value if it's not an empty string, parses to a valid number, is between 0 and 100 (exclusive) + // and is an integer + const inputValueIsValid = isValidNumber(inputValue, true, 99, 1); + + useDebounceWithOptions( + () => { + if (!inputValueIsValid) return; + updateLayer({ + ...layer, + columns: { + ...layer.columns, + [columnId]: { + ...currentColumn, + label: currentColumn.customLabel + ? currentColumn.label + : ofName( + indexPattern.getFieldByName(currentColumn.sourceField)?.displayName || + currentColumn.sourceField, + inputValueAsNumber + ), + params: { + ...currentColumn.params, + percentile: inputValueAsNumber, + }, + }, + }, + }); + }, + { skipFirstRender: true }, + 256, + [inputValue] + ); + + const handleInputChange = useCallback((e: React.ChangeEvent) => { + const val = String(e.target.value); + setInputValue(val); + }, []); + return ( + + + + ); + }, +}; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx index 9ab677bf68f62..420846f7fc801 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx @@ -22,7 +22,7 @@ import { keys, } from '@elastic/eui'; import { IFieldFormat } from '../../../../../../../../src/plugins/data/common'; -import { RangeTypeLens, isValidRange, isValidNumber } from './ranges'; +import { RangeTypeLens, isValidRange } from './ranges'; import { FROM_PLACEHOLDER, TO_PLACEHOLDER, TYPING_DEBOUNCE_TIME } from './constants'; import { NewBucketButton, @@ -30,7 +30,7 @@ import { DraggableBucketContainer, LabelInput, } from '../shared_components'; -import { useDebounceWithOptions } from '../helpers'; +import { isValidNumber, useDebounceWithOptions } from '../helpers'; const generateId = htmlIdGenerator(); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx index c2c52985c6cd2..987c8971aa310 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx @@ -142,7 +142,8 @@ describe('ranges', () => { const esAggsFn = rangeOperation.toEsAggsFn( layer.columns.col1 as RangeIndexPatternColumn, 'col1', - {} as IndexPattern + {} as IndexPattern, + layer ); expect(esAggsFn).toMatchInlineSnapshot(` Object { @@ -184,7 +185,8 @@ describe('ranges', () => { const esAggsFn = rangeOperation.toEsAggsFn( layer.columns.col1 as RangeIndexPatternColumn, 'col1', - {} as IndexPattern + {} as IndexPattern, + layer ); expect(esAggsFn).toEqual( @@ -203,7 +205,8 @@ describe('ranges', () => { const esAggsFn = rangeOperation.toEsAggsFn( layer.columns.col1 as RangeIndexPatternColumn, 'col1', - {} as IndexPattern + {} as IndexPattern, + layer ); expect(esAggsFn).toEqual( @@ -222,7 +225,8 @@ describe('ranges', () => { const esAggsFn = rangeOperation.toEsAggsFn( layer.columns.col1 as RangeIndexPatternColumn, 'col1', - {} as IndexPattern + {} as IndexPattern, + layer ); expect((esAggsFn as { arguments: unknown }).arguments).toEqual( diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx index 2ba8f5febce5b..aa5cc8255a584 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx @@ -19,7 +19,7 @@ import { updateColumnParam } from '../../layer_helpers'; import { supportedFormats } from '../../../format_column'; import { MODES, AUTO_BARS, DEFAULT_INTERVAL, MIN_HISTOGRAM_BARS, SLICES } from './constants'; import { IndexPattern, IndexPatternField } from '../../../types'; -import { getInvalidFieldMessage } from '../helpers'; +import { getInvalidFieldMessage, isValidNumber } from '../helpers'; type RangeType = Omit; // Try to cover all possible serialized states for ranges @@ -52,10 +52,6 @@ export type UpdateParamsFnType = ( value: RangeColumnParams[K] ) => void; -// on initialization values can be null (from the Infinity serialization), so handle it correctly -// or they will be casted to 0 by the editor ( see #78867 ) -export const isValidNumber = (value: number | '' | null): value is number => - value != null && value !== '' && !isNaN(value) && isFinite(value); export const isRangeWithin = (range: RangeType): boolean => range.from <= range.to; const isFullRange = (range: RangeTypeLens): range is FullRangeTypeLens => isValidNumber(range.from) && isValidNumber(range.to); @@ -98,7 +94,10 @@ export const rangeOperation: OperationDefinition - indexPattern.getFieldByName(column.sourceField)!.displayName, + indexPattern.getFieldByName(column.sourceField)?.displayName ?? + i18n.translate('xpack.lens.indexPattern.missingFieldLabel', { + defaultMessage: 'Missing field', + }), buildColumn({ field }) { return { label: field.displayName, @@ -149,10 +148,10 @@ export const rangeOperation: OperationDefinition = { label: range.label }; // be careful with the fields to set on partial ranges if (isValidNumber(range.from)) { - partialRange.from = range.from; + partialRange.from = Number(range.from); } if (isValidNumber(range.to)) { - partialRange.to = range.to; + partialRange.to = Number(range.to); } return partialRange; }) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx index 888df40873a35..625084000fa93 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx @@ -18,23 +18,36 @@ import { } from '@elastic/eui'; import { AggFunctionsMapping } from '../../../../../../../../src/plugins/data/public'; import { buildExpressionFunction } from '../../../../../../../../src/plugins/expressions/public'; -import { IndexPatternColumn } from '../../../indexpattern'; import { updateColumnParam, isReferenced } from '../../layer_helpers'; import { DataType } from '../../../../types'; import { OperationDefinition } from '../index'; import { FieldBasedIndexPatternColumn } from '../column_types'; import { ValuesRangeInput } from './values_range_input'; -import { getInvalidFieldMessage } from '../helpers'; +import { getEsAggsSuffix, getInvalidFieldMessage } from '../helpers'; +import type { IndexPatternLayer } from '../../../types'; -function ofName(name: string) { +function ofName(name?: string) { return i18n.translate('xpack.lens.indexPattern.termsOf', { defaultMessage: 'Top values of {name}', - values: { name }, + values: { + name: + name ?? + i18n.translate('xpack.lens.indexPattern.missingFieldLabel', { + defaultMessage: 'Missing field', + }), + }, }); } -function isSortableByColumn(column: IndexPatternColumn) { - return !column.isBucketed && column.operationType !== 'last_value'; +function isSortableByColumn(layer: IndexPatternLayer, columnId: string) { + const column = layer.columns[columnId]; + return ( + column && + !column.isBucketed && + column.operationType !== 'last_value' && + !('references' in column) && + !isReferenced(layer, columnId) + ); } const DEFAULT_SIZE = 3; @@ -89,10 +102,7 @@ export const termsOperation: OperationDefinition - column && !isReferenced(layer, columnId) && isSortableByColumn(column) - ) + .filter(([columnId]) => isSortableByColumn(layer, columnId)) .map(([id]) => id)[0]; const previousBucketsLength = Object.values(layer.columns).filter( @@ -109,7 +119,10 @@ export const termsOperation: OperationDefinition { + toEsAggsFn: (column, columnId, _indexPattern, layer) => { return buildExpressionFunction('aggTerms', { id: columnId, enabled: true, schema: 'segment', field: column.sourceField, orderBy: - column.params.orderBy.type === 'alphabetical' ? '_key' : column.params.orderBy.columnId, + column.params.orderBy.type === 'alphabetical' + ? '_key' + : `${column.params.orderBy.columnId}${getEsAggsSuffix( + layer.columns[column.params.orderBy.columnId] + )}`, order: column.params.orderDirection, size: column.params.size, otherBucket: Boolean(column.params.otherBucket), @@ -138,7 +155,7 @@ export const termsOperation: OperationDefinition - ofName(indexPattern.getFieldByName(column.sourceField)!.displayName), + ofName(indexPattern.getFieldByName(column.sourceField)?.displayName), onFieldChange: (oldColumn, field) => { const newParams = { ...oldColumn.params }; if ('format' in newParams && field.type !== 'number') { @@ -152,11 +169,13 @@ export const termsOperation: OperationDefinition { + onOtherColumnChanged: (layer, thisColumnId, changedColumnId) => { + const columns = layer.columns; + const currentColumn = columns[thisColumnId] as TermsIndexPatternColumn; if (currentColumn.params.orderBy.type === 'column') { // check whether the column is still there and still a metric const columnSortedBy = columns[currentColumn.params.orderBy.columnId]; - if (!columnSortedBy || !isSortableByColumn(columnSortedBy)) { + if (!columnSortedBy || !isSortableByColumn(layer, changedColumnId)) { return { ...currentColumn, params: { @@ -194,7 +213,7 @@ export const termsOperation: OperationDefinition isSortableByColumn(column)) + .filter(([sortId]) => isSortableByColumn(layer, sortId)) .map(([sortId, column]) => { return { value: toValue({ type: 'column', columnId: sortId }), diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx index eb78bb3ffebff..d60992bda2e2a 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx @@ -65,7 +65,8 @@ describe('terms', () => { const esAggsFn = termsOperation.toEsAggsFn( { ...termsColumn, params: { ...termsColumn.params, otherBucket: true } }, 'col1', - {} as IndexPattern + {} as IndexPattern, + layer ); expect(esAggsFn).toEqual( expect.objectContaining({ @@ -87,7 +88,8 @@ describe('terms', () => { params: { ...termsColumn.params, otherBucket: false, missingBucket: true }, }, 'col1', - {} as IndexPattern + {} as IndexPattern, + layer ); expect(esAggsFn).toEqual( expect.objectContaining({ @@ -98,6 +100,45 @@ describe('terms', () => { }) ); }); + + it('should include esaggs suffix from other columns in orderby argument', () => { + const termsColumn = layer.columns.col1 as TermsIndexPatternColumn; + const esAggsFn = termsOperation.toEsAggsFn( + { + ...termsColumn, + params: { + ...termsColumn.params, + otherBucket: true, + orderBy: { type: 'column', columnId: 'abcde' }, + }, + }, + 'col1', + {} as IndexPattern, + { + ...layer, + columns: { + ...layer.columns, + abcde: { + dataType: 'number', + isBucketed: false, + operationType: 'percentile', + sourceField: 'abc', + label: '', + params: { + percentile: 12, + }, + }, + }, + } + ); + expect(esAggsFn).toEqual( + expect.objectContaining({ + arguments: expect.objectContaining({ + orderBy: ['abcde.12'], + }), + }) + ); + }); }); describe('onFieldChange', () => { @@ -402,15 +443,25 @@ describe('terms', () => { }, sourceField: 'category', }; - const updatedColumn = termsOperation.onOtherColumnChanged!(initialColumn, { - col1: { - label: 'Count', - dataType: 'number', - isBucketed: false, - sourceField: 'Records', - operationType: 'count', + const updatedColumn = termsOperation.onOtherColumnChanged!( + { + indexPatternId: '', + columnOrder: [], + columns: { + col2: initialColumn, + col1: { + label: 'Count', + dataType: 'number', + isBucketed: false, + sourceField: 'Records', + operationType: 'count', + }, + }, }, - }); + 'col2', + 'col1' + ); + expect(updatedColumn).toBe(initialColumn); }); @@ -429,18 +480,74 @@ describe('terms', () => { }, sourceField: 'category', }; - const updatedColumn = termsOperation.onOtherColumnChanged!(initialColumn, { - col1: { - label: 'Last Value', - dataType: 'number', - isBucketed: false, - sourceField: 'bytes', - operationType: 'last_value', - params: { - sortField: 'time', + const updatedColumn = termsOperation.onOtherColumnChanged!( + { + columns: { + col2: initialColumn, + col1: { + label: 'Last Value', + dataType: 'number', + isBucketed: false, + sourceField: 'bytes', + operationType: 'last_value', + params: { + sortField: 'time', + }, + }, }, + columnOrder: [], + indexPatternId: '', }, - }); + 'col2', + 'col1' + ); + expect(updatedColumn.params).toEqual( + expect.objectContaining({ + orderBy: { type: 'alphabetical' }, + }) + ); + }); + + it('should switch to alphabetical ordering if metric is reference-based', () => { + const initialColumn: TermsIndexPatternColumn = { + label: 'Top value of category', + dataType: 'string', + isBucketed: true, + + // Private + operationType: 'terms', + params: { + orderBy: { type: 'column', columnId: 'col1' }, + size: 3, + orderDirection: 'asc', + }, + sourceField: 'category', + }; + const updatedColumn = termsOperation.onOtherColumnChanged!( + { + columns: { + col2: initialColumn, + col1: { + label: 'Cumulative sum', + dataType: 'number', + isBucketed: false, + operationType: 'cumulative_sum', + references: ['referenced'], + }, + referenced: { + label: '', + dataType: 'number', + isBucketed: false, + operationType: 'count', + sourceField: 'Records', + }, + }, + columnOrder: [], + indexPatternId: '', + }, + 'col2', + 'col1' + ); expect(updatedColumn.params).toEqual( expect.objectContaining({ orderBy: { type: 'alphabetical' }, @@ -451,20 +558,27 @@ describe('terms', () => { it('should switch to alphabetical ordering if there are no columns to order by', () => { const termsColumn = termsOperation.onOtherColumnChanged!( { - label: 'Top value of category', - dataType: 'string', - isBucketed: true, + columns: { + col2: { + label: 'Top value of category', + dataType: 'string', + isBucketed: true, - // Private - operationType: 'terms', - params: { - orderBy: { type: 'column', columnId: 'col1' }, - size: 3, - orderDirection: 'asc', + // Private + operationType: 'terms', + params: { + orderBy: { type: 'column', columnId: 'col1' }, + size: 3, + orderDirection: 'asc', + }, + sourceField: 'category', + }, }, - sourceField: 'category', + columnOrder: [], + indexPatternId: '', }, - {} + 'col2', + 'col1' ); expect(termsColumn.params).toEqual( expect.objectContaining({ @@ -476,33 +590,39 @@ describe('terms', () => { it('should switch to alphabetical ordering if the order column is not a metric anymore', () => { const termsColumn = termsOperation.onOtherColumnChanged!( { - label: 'Top value of category', - dataType: 'string', - isBucketed: true, + columns: { + col2: { + label: 'Top value of category', + dataType: 'string', + isBucketed: true, - // Private - operationType: 'terms', - params: { - orderBy: { type: 'column', columnId: 'col1' }, - size: 3, - orderDirection: 'asc', - }, - sourceField: 'category', - }, - { - col1: { - label: 'Value of timestamp', - dataType: 'date', - isBucketed: true, + // Private + operationType: 'terms', + params: { + orderBy: { type: 'column', columnId: 'col1' }, + size: 3, + orderDirection: 'asc', + }, + sourceField: 'category', + }, + col1: { + label: 'Value of timestamp', + dataType: 'date', + isBucketed: true, - // Private - operationType: 'date_histogram', - params: { - interval: 'w', + // Private + operationType: 'date_histogram', + params: { + interval: 'w', + }, + sourceField: 'timestamp', }, - sourceField: 'timestamp', }, - } + columnOrder: [], + indexPatternId: '', + }, + 'col2', + 'col1' ); expect(termsColumn.params).toEqual( expect.objectContaining({ diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/index.ts index 7123becf71b4d..079913347470a 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/index.ts @@ -12,6 +12,7 @@ export { IndexPatternColumn, FieldBasedIndexPatternColumn, IncompleteColumn, + RequiredReference, } from './definitions'; export { createMockedReferenceOperation } from './mocks'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts index bb09474798fd4..9496f95f74dec 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts @@ -190,6 +190,44 @@ describe('state_helpers', () => { ).toEqual(expect.objectContaining({ columnOrder: ['col1', 'col2'] })); }); + it('should insert a metric after buckets, but before references', () => { + const layer: IndexPatternLayer = { + indexPatternId: '1', + columnOrder: ['col1'], + columns: { + col1: { + label: 'Date histogram of timestamp', + dataType: 'date', + isBucketed: true, + + // Private + operationType: 'date_histogram', + sourceField: 'timestamp', + params: { + interval: 'h', + }, + }, + col3: { + label: 'Reference', + dataType: 'number', + isBucketed: false, + + operationType: 'cumulative_sum', + references: ['col2'], + }, + }, + }; + expect( + insertNewColumn({ + layer, + indexPattern, + columnId: 'col2', + op: 'count', + field: documentField, + }) + ).toEqual(expect.objectContaining({ columnOrder: ['col1', 'col2', 'col3'] })); + }); + it('should insert new buckets at the end of previous buckets', () => { const layer: IndexPatternLayer = { indexPatternId: '1', @@ -782,18 +820,83 @@ describe('state_helpers', () => { field: indexPattern.fields[2], // bytes field }); - expect(operationDefinitionMap.terms.onOtherColumnChanged).toHaveBeenCalledWith(termsColumn, { - col1: termsColumn, - col2: expect.objectContaining({ - label: 'Average of bytes', - dataType: 'number', - isBucketed: false, + expect(operationDefinitionMap.terms.onOtherColumnChanged).toHaveBeenCalledWith( + { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + col1: termsColumn, + col2: expect.objectContaining({ + label: 'Average of bytes', + dataType: 'number', + isBucketed: false, + sourceField: 'bytes', + operationType: 'avg', + }), + }, + incompleteColumns: {}, + }, + 'col1', + 'col2' + ); + }); - // Private - operationType: 'avg', - sourceField: 'bytes', - }), + it('should execute adjustments for other columns when creating a reference', () => { + const termsColumn: TermsIndexPatternColumn = { + label: 'Top values of source', + dataType: 'string', + isBucketed: true, + + // Private + operationType: 'terms', + sourceField: 'source', + params: { + orderBy: { type: 'column', columnId: 'willBeReference' }, + orderDirection: 'desc', + size: 5, + }, + }; + + replaceColumn({ + layer: { + indexPatternId: '1', + columnOrder: ['col1', 'willBeReference'], + columns: { + col1: termsColumn, + willBeReference: { + label: 'Count', + dataType: 'number', + isBucketed: false, + sourceField: 'Records', + operationType: 'count', + }, + }, + }, + indexPattern, + columnId: 'willBeReference', + op: 'cumulative_sum', }); + + expect(operationDefinitionMap.terms.onOtherColumnChanged).toHaveBeenCalledWith( + { + indexPatternId: '1', + columnOrder: ['col1', 'willBeReference'], + columns: { + col1: { + ...termsColumn, + params: { orderBy: { type: 'alphabetical' }, orderDirection: 'asc', size: 5 }, + }, + willBeReference: expect.objectContaining({ + dataType: 'number', + isBucketed: false, + operationType: 'cumulative_sum', + }), + }, + incompleteColumns: {}, + }, + 'col1', + 'willBeReference' + ); }); it('should not wrap the previous operation when switching to reference', () => { @@ -963,7 +1066,7 @@ describe('state_helpers', () => { isTransferable: jest.fn(), toExpression: jest.fn().mockReturnValue([]), getPossibleOperation: jest.fn().mockReturnValue({ dataType: 'number', isBucketed: false }), - getDefaultLabel: () => 'Test reference', + getDefaultLabel: jest.fn().mockReturnValue('Test reference'), }; const layer: IndexPatternLayer = { @@ -1081,6 +1184,7 @@ describe('state_helpers', () => { }, }, columnId: 'col1', + indexPattern, }) ).toEqual({ indexPatternId: '1', @@ -1126,6 +1230,7 @@ describe('state_helpers', () => { }, }, columnId: 'col2', + indexPattern, }) ).toEqual({ indexPatternId: '1', @@ -1176,11 +1281,14 @@ describe('state_helpers', () => { }, }, columnId: 'col2', + indexPattern, }); - expect(operationDefinitionMap.terms.onOtherColumnChanged).toHaveBeenCalledWith(termsColumn, { - col1: termsColumn, - }); + expect(operationDefinitionMap.terms.onOtherColumnChanged).toHaveBeenCalledWith( + { indexPatternId: '1', columnOrder: ['col1', 'col2'], columns: { col1: termsColumn } }, + 'col1', + 'col2' + ); }); it('should delete the column and all of its references', () => { @@ -1207,11 +1315,57 @@ describe('state_helpers', () => { }, }, }; - expect(deleteColumn({ layer, columnId: 'col2' })).toEqual( + expect(deleteColumn({ layer, columnId: 'col2', indexPattern })).toEqual( expect.objectContaining({ columnOrder: [], columns: {} }) ); }); + it('should update the labels when deleting columns', () => { + const layer: IndexPatternLayer = { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + col1: { + label: 'Count', + dataType: 'number', + isBucketed: false, + + operationType: 'count', + sourceField: 'Records', + }, + col2: { + label: 'Changed label', + dataType: 'number', + isBucketed: false, + + // @ts-expect-error not a valid type + operationType: 'testReference', + references: ['col1'], + }, + }, + }; + deleteColumn({ layer, columnId: 'col1', indexPattern }); + expect(operationDefinitionMap.testReference.getDefaultLabel).toHaveBeenCalledWith( + { + label: 'Changed label', + dataType: 'number', + isBucketed: false, + operationType: 'testReference', + references: ['col1'], + }, + indexPattern, + { + col2: { + label: 'Default label', + dataType: 'number', + isBucketed: false, + operationType: 'testReference', + references: ['col1'], + }, + } + ); + }); + it('should recursively delete references', () => { const layer: IndexPatternLayer = { indexPatternId: '1', @@ -1245,7 +1399,7 @@ describe('state_helpers', () => { }, }, }; - expect(deleteColumn({ layer, columnId: 'col3' })).toEqual( + expect(deleteColumn({ layer, columnId: 'col3', indexPattern })).toEqual( expect.objectContaining({ columnOrder: [], columns: {} }) ); }); @@ -1680,63 +1834,34 @@ describe('state_helpers', () => { }); describe('getErrorMessages', () => { - it('should collect errors from the operation definitions', () => { + it('should collect errors from metric-type operation definitions', () => { const mock = jest.fn().mockReturnValue(['error 1']); - operationDefinitionMap.testReference.getErrorMessage = mock; + operationDefinitionMap.avg.getErrorMessage = mock; const errors = getErrorMessages({ indexPatternId: '1', columnOrder: [], columns: { - col1: - // @ts-expect-error not statically analyzed - { operationType: 'testReference', references: [] }, + // @ts-expect-error invalid column + col1: { operationType: 'avg' }, }, }); expect(mock).toHaveBeenCalled(); expect(errors).toHaveLength(1); }); - it('should identify missing references', () => { + it('should collect errors from reference-type operation definitions', () => { + const mock = jest.fn().mockReturnValue(['error 1']); + operationDefinitionMap.testReference.getErrorMessage = mock; const errors = getErrorMessages({ indexPatternId: '1', columnOrder: [], columns: { col1: - // @ts-expect-error not statically analyzed yet - { operationType: 'testReference', references: ['ref1', 'ref2'] }, - }, - }); - expect(errors).toHaveLength(2); - }); - - it('should identify references that are no longer valid', () => { - // There is only one operation with `none` as the input type - // @ts-expect-error this function is not valid - operationDefinitionMap.testReference.requiredReferences = [ - { - input: ['none'], - validateMetadata: () => true, - }, - ]; - - const errors = getErrorMessages({ - indexPatternId: '1', - columnOrder: [], - columns: { - // @ts-expect-error incomplete operation - ref1: { - dataType: 'string', - isBucketed: true, - operationType: 'terms', - }, - col1: { - label: '', - references: ['ref1'], - // @ts-expect-error tests only - operationType: 'testReference', - }, + // @ts-expect-error not statically analyzed + { operationType: 'testReference', references: [] }, }, }); + expect(mock).toHaveBeenCalled(); expect(errors).toHaveLength(1); }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts index 1619ad907fffc..2d8078b9a6154 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts @@ -5,7 +5,6 @@ */ import _, { partition } from 'lodash'; -import { i18n } from '@kbn/i18n'; import { operationDefinitionMap, operationDefinitions, @@ -61,9 +60,15 @@ export function insertNewColumn({ const possibleOperation = operationDefinition.getPossibleOperation(); const isBucketed = Boolean(possibleOperation.isBucketed); if (isBucketed) { - return addBucket(layer, operationDefinition.buildColumn({ ...baseOptions, layer }), columnId); + return updateDefaultLabels( + addBucket(layer, operationDefinition.buildColumn({ ...baseOptions, layer }), columnId), + indexPattern + ); } else { - return addMetric(layer, operationDefinition.buildColumn({ ...baseOptions, layer }), columnId); + return updateDefaultLabels( + addMetric(layer, operationDefinition.buildColumn({ ...baseOptions, layer }), columnId), + indexPattern + ); } } @@ -77,7 +82,7 @@ export function insertNewColumn({ // access to the operationSupportMatrix, we should validate the metadata against // the possible fields const validOperations = Object.values(operationDefinitionMap).filter(({ type }) => - isOperationAllowedAsReference({ validation, operationType: type }) + isOperationAllowedAsReference({ validation, operationType: type, indexPattern }) ); if (!validOperations.length) { @@ -122,29 +127,23 @@ export function insertNewColumn({ return newId; }); - const possibleOperation = operationDefinition.getPossibleOperation(); - const isBucketed = Boolean(possibleOperation.isBucketed); - if (isBucketed) { - return addBucket( - tempLayer, - operationDefinition.buildColumn({ - ...baseOptions, - layer: tempLayer, - referenceIds, - }), - columnId + const possibleOperation = operationDefinition.getPossibleOperation(indexPattern); + if (!possibleOperation) { + throw new Error( + `Can't create operation ${op} because it's incompatible with the index pattern` ); - } else { - return addMetric( + } + const isBucketed = Boolean(possibleOperation.isBucketed); + + const addOperationFn = isBucketed ? addBucket : addMetric; + return updateDefaultLabels( + addOperationFn( tempLayer, - operationDefinition.buildColumn({ - ...baseOptions, - layer: tempLayer, - referenceIds, - }), + operationDefinition.buildColumn({ ...baseOptions, layer: tempLayer, referenceIds }), columnId - ); - } + ), + indexPattern + ); } const invalidFieldName = (layer.incompleteColumns ?? {})[columnId]?.sourceField; @@ -159,16 +158,22 @@ export function insertNewColumn({ } const isBucketed = Boolean(possibleOperation.isBucketed); if (isBucketed) { - return addBucket( - layer, - operationDefinition.buildColumn({ ...baseOptions, layer, field: invalidField }), - columnId + return updateDefaultLabels( + addBucket( + layer, + operationDefinition.buildColumn({ ...baseOptions, layer, field: invalidField }), + columnId + ), + indexPattern ); } else { - return addMetric( - layer, - operationDefinition.buildColumn({ ...baseOptions, layer, field: invalidField }), - columnId + return updateDefaultLabels( + addMetric( + layer, + operationDefinition.buildColumn({ ...baseOptions, layer, field: invalidField }), + columnId + ), + indexPattern ); } } else if (!field) { @@ -193,19 +198,15 @@ export function insertNewColumn({ }; } const isBucketed = Boolean(possibleOperation.isBucketed); - if (isBucketed) { - return addBucket( + const addOperationFn = isBucketed ? addBucket : addMetric; + return updateDefaultLabels( + addOperationFn( layer, operationDefinition.buildColumn({ ...baseOptions, layer, field }), columnId - ); - } else { - return addMetric( - layer, - operationDefinition.buildColumn({ ...baseOptions, layer, field }), - columnId - ); - } + ), + indexPattern + ); } export function replaceColumn({ @@ -241,39 +242,50 @@ export function replaceColumn({ if (previousDefinition.input === 'fullReference') { (previousColumn as ReferenceBasedIndexPatternColumn).references.forEach((id: string) => { - tempLayer = deleteColumn({ layer: tempLayer, columnId: id }); + tempLayer = deleteColumn({ layer: tempLayer, columnId: id, indexPattern }); }); } + tempLayer = resetIncomplete(tempLayer, columnId); + if (operationDefinition.input === 'fullReference') { const referenceIds = operationDefinition.requiredReferences.map(() => generateId()); - const newColumns = { - ...tempLayer.columns, - [columnId]: operationDefinition.buildColumn({ - ...baseOptions, - layer: tempLayer, - referenceIds, - previousColumn, - }), - }; - return { + const newLayer = { ...tempLayer, - columnOrder: getColumnOrder({ ...tempLayer, columns: newColumns }), - columns: newColumns, + columns: { + ...tempLayer.columns, + [columnId]: operationDefinition.buildColumn({ + ...baseOptions, + layer: tempLayer, + referenceIds, + previousColumn, + }), + }, }; + return updateDefaultLabels( + { + ...tempLayer, + columnOrder: getColumnOrder(newLayer), + columns: adjustColumnReferencesForChangedColumn(newLayer, columnId), + }, + indexPattern + ); } if (operationDefinition.input === 'none') { let newColumn = operationDefinition.buildColumn({ ...baseOptions, layer: tempLayer }); newColumn = adjustLabel(newColumn, previousColumn); - const newColumns = { ...tempLayer.columns, [columnId]: newColumn }; - return { - ...tempLayer, - columnOrder: getColumnOrder({ ...tempLayer, columns: newColumns }), - columns: adjustColumnReferencesForChangedColumn(newColumns, columnId), - }; + const newLayer = { ...tempLayer, columns: { ...tempLayer.columns, [columnId]: newColumn } }; + return updateDefaultLabels( + { + ...tempLayer, + columnOrder: getColumnOrder(newLayer), + columns: adjustColumnReferencesForChangedColumn(newLayer, columnId), + }, + indexPattern + ); } if (!field) { @@ -289,12 +301,15 @@ export function replaceColumn({ let newColumn = operationDefinition.buildColumn({ ...baseOptions, layer: tempLayer, field }); newColumn = adjustLabel(newColumn, previousColumn); - const newColumns = { ...tempLayer.columns, [columnId]: newColumn }; - return { - ...tempLayer, - columnOrder: getColumnOrder({ ...tempLayer, columns: newColumns }), - columns: adjustColumnReferencesForChangedColumn(newColumns, columnId), - }; + const newLayer = { ...tempLayer, columns: { ...tempLayer.columns, [columnId]: newColumn } }; + return updateDefaultLabels( + { + ...tempLayer, + columnOrder: getColumnOrder(newLayer), + columns: adjustColumnReferencesForChangedColumn(newLayer, columnId), + }, + indexPattern + ); } else if ( operationDefinition.input === 'field' && field && @@ -304,12 +319,20 @@ export function replaceColumn({ // Same operation, new field const newColumn = operationDefinition.onFieldChange(previousColumn, field); - const newColumns = { ...layer.columns, [columnId]: adjustLabel(newColumn, previousColumn) }; - return { - ...layer, - columnOrder: getColumnOrder({ ...layer, columns: newColumns }), - columns: adjustColumnReferencesForChangedColumn(newColumns, columnId), - }; + if (previousColumn.customLabel) { + newColumn.customLabel = true; + newColumn.label = previousColumn.label; + } + + const newLayer = { ...layer, columns: { ...layer.columns, [columnId]: newColumn } }; + return updateDefaultLabels( + { + ...resetIncomplete(layer, columnId), + columnOrder: getColumnOrder(newLayer), + columns: adjustColumnReferencesForChangedColumn(newLayer, columnId), + }, + indexPattern + ); } else { throw new Error('nothing changed'); } @@ -370,7 +393,6 @@ function addMetric( ...layer.columns, [addedColumnId]: column, }, - columnOrder: [...layer.columnOrder, addedColumnId], }; return { ...tempLayer, columnOrder: getColumnOrder(tempLayer) }; } @@ -409,17 +431,18 @@ export function updateColumnParam({ }; } -function adjustColumnReferencesForChangedColumn( - columns: Record, - columnId: string -) { - const newColumns = { ...columns }; +function adjustColumnReferencesForChangedColumn(layer: IndexPatternLayer, changedColumnId: string) { + const newColumns = { ...layer.columns }; Object.keys(newColumns).forEach((currentColumnId) => { - if (currentColumnId !== columnId) { + if (currentColumnId !== changedColumnId) { const currentColumn = newColumns[currentColumnId]; const operationDefinition = operationDefinitionMap[currentColumn.operationType]; newColumns[currentColumnId] = operationDefinition.onOtherColumnChanged - ? operationDefinition.onOtherColumnChanged(currentColumn, newColumns) + ? operationDefinition.onOtherColumnChanged( + { ...layer, columns: newColumns }, + currentColumnId, + changedColumnId + ) : currentColumn; } }); @@ -429,9 +452,11 @@ function adjustColumnReferencesForChangedColumn( export function deleteColumn({ layer, columnId, + indexPattern, }: { layer: IndexPatternLayer; columnId: string; + indexPattern: IndexPattern; }): IndexPatternLayer { const column = layer.columns[columnId]; if (!column) { @@ -451,17 +476,27 @@ export function deleteColumn({ let newLayer = { ...layer, - columns: adjustColumnReferencesForChangedColumn(hypotheticalColumns, columnId), + columns: adjustColumnReferencesForChangedColumn( + { ...layer, columns: hypotheticalColumns }, + columnId + ), }; extraDeletions.forEach((id) => { - newLayer = deleteColumn({ layer: newLayer, columnId: id }); + newLayer = deleteColumn({ layer: newLayer, columnId: id, indexPattern }); }); const newIncomplete = { ...(newLayer.incompleteColumns || {}) }; delete newIncomplete[columnId]; - return { ...newLayer, columnOrder: getColumnOrder(newLayer), incompleteColumns: newIncomplete }; + return updateDefaultLabels( + { + ...newLayer, + columnOrder: getColumnOrder(newLayer), + incompleteColumns: newIncomplete, + }, + indexPattern + ); } // Derives column order from column object, respects existing columnOrder @@ -482,7 +517,7 @@ export function getColumnOrder(layer: IndexPatternLayer): string[] { const [direct, referenceBased] = _.partition( entries, - ([id, col]) => operationDefinitionMap[col.operationType].input !== 'fullReference' + ([, col]) => operationDefinitionMap[col.operationType].input !== 'fullReference' ); // If a reference has another reference as input, put it last in sort order referenceBased.sort(([idA, a], [idB, b]) => { @@ -503,7 +538,7 @@ export function getColumnOrder(layer: IndexPatternLayer): string[] { } // Splits existing columnOrder into the three categories -function getExistingColumnGroups(layer: IndexPatternLayer): [string[], string[], string[]] { +export function getExistingColumnGroups(layer: IndexPatternLayer): [string[], string[], string[]] { const [direct, referenced] = partition( layer.columnOrder, (columnId) => layer.columns[columnId] && !('references' in layer.columns[columnId]) @@ -553,44 +588,9 @@ export function getErrorMessages(layer: IndexPatternLayer): string[] | undefined Object.entries(layer.columns).forEach(([columnId, column]) => { const def = operationDefinitionMap[column.operationType]; - if (def.input === 'fullReference' && def.getErrorMessage) { + if (def.getErrorMessage) { errors.push(...(def.getErrorMessage(layer, columnId) ?? [])); } - - if ('references' in column) { - column.references.forEach((referenceId, index) => { - if (!layer.columns[referenceId]) { - errors.push( - i18n.translate('xpack.lens.indexPattern.missingReferenceError', { - defaultMessage: 'Dimension {dimensionLabel} is incomplete', - values: { - dimensionLabel: column.label, - }, - }) - ); - } else { - const referenceColumn = layer.columns[referenceId]!; - const requirements = - // @ts-expect-error not statically analyzed - operationDefinitionMap[column.operationType].requiredReferences[index]; - const isValid = isColumnValidAsReference({ - validation: requirements, - column: referenceColumn, - }); - - if (!isValid) { - errors.push( - i18n.translate('xpack.lens.indexPattern.invalidReferenceConfiguration', { - defaultMessage: 'Dimension {dimensionLabel} does not have a valid configuration', - values: { - dimensionLabel: column.label, - }, - }) - ); - } - } - }); - } }); return errors.length ? errors : undefined; @@ -603,30 +603,15 @@ export function isReferenced(layer: IndexPatternLayer, columnId: string): boolea return allReferences.includes(columnId); } -function isColumnValidAsReference({ - column, - validation, -}: { - column: IndexPatternColumn; - validation: RequiredReference; -}): boolean { - if (!column) return false; - const operationType = column.operationType; - const operationDefinition = operationDefinitionMap[operationType]; - return ( - validation.input.includes(operationDefinition.input) && - (!validation.specificOperations || validation.specificOperations.includes(operationType)) && - validation.validateMetadata(column) - ); -} - -function isOperationAllowedAsReference({ +export function isOperationAllowedAsReference({ operationType, validation, field, + indexPattern, }: { operationType: OperationType; validation: RequiredReference; + indexPattern: IndexPattern; field?: IndexPatternField; }): boolean { const operationDefinition = operationDefinitionMap[operationType]; @@ -635,9 +620,12 @@ function isOperationAllowedAsReference({ if (field && operationDefinition.input === 'field') { const metadata = operationDefinition.getPossibleOperationForField(field); hasValidMetadata = Boolean(metadata) && validation.validateMetadata(metadata!); - } else if (operationDefinition.input !== 'field') { + } else if (operationDefinition.input === 'none') { const metadata = operationDefinition.getPossibleOperation(); hasValidMetadata = Boolean(metadata) && validation.validateMetadata(metadata!); + } else if (operationDefinition.input === 'fullReference') { + const metadata = operationDefinition.getPossibleOperation(indexPattern); + hasValidMetadata = Boolean(metadata) && validation.validateMetadata(metadata!); } else { // TODO: How can we validate the metadata without a specific field? } @@ -648,6 +636,29 @@ function isOperationAllowedAsReference({ ); } +// Labels need to be updated when columns are added because reference-based column labels +// are sometimes copied into the parents +function updateDefaultLabels( + layer: IndexPatternLayer, + indexPattern: IndexPattern +): IndexPatternLayer { + const copiedColumns = { ...layer.columns }; + layer.columnOrder.forEach((id) => { + const col = copiedColumns[id]; + if (!col.customLabel) { + copiedColumns[id] = { + ...col, + label: operationDefinitionMap[col.operationType].getDefaultLabel( + col, + indexPattern, + copiedColumns + ), + }; + } + }); + return { ...layer, columns: copiedColumns }; +} + export function resetIncomplete(layer: IndexPatternLayer, columnId: string): IndexPatternLayer { const incompleteColumns = { ...(layer.incompleteColumns ?? {}) }; delete incompleteColumns[columnId]; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.test.ts index 9f2b8eab4e09b..882252132c5b3 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.test.ts @@ -293,6 +293,11 @@ describe('getOperationTypesForField', () => { "operationType": "median", "type": "field", }, + Object { + "field": "bytes", + "operationType": "percentile", + "type": "field", + }, Object { "field": "bytes", "operationType": "last_value", diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.ts index 58685fa494a04..c111983ea2cd6 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.ts @@ -167,10 +167,13 @@ export function getAvailableOperationsByMetadata(indexPattern: IndexPattern) { operationDefinition.getPossibleOperation() ); } else if (operationDefinition.input === 'fullReference') { - addToMap( - { type: 'fullReference', operationType: operationDefinition.type }, - operationDefinition.getPossibleOperation() - ); + const validOperation = operationDefinition.getPossibleOperation(indexPattern); + if (validOperation) { + addToMap( + { type: 'fullReference', operationType: operationDefinition.type }, + validOperation + ); + } } }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.test.ts index 841011c588433..09132b142986f 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.test.ts @@ -4,8 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { TimeScaleUnit } from '../time_scale'; -import { IndexPatternColumn } from './definitions'; +import type { IndexPatternLayer } from '../types'; +import type { TimeScaleUnit } from '../time_scale'; +import type { IndexPatternColumn } from './definitions'; import { adjustTimeScaleLabelSuffix, adjustTimeScaleOnOtherColumnChange } from './time_scale_utils'; export const DEFAULT_TIME_SCALE = 's' as TimeScaleUnit; @@ -48,45 +49,71 @@ describe('time scale utils', () => { isBucketed: false, timeScale: 's', }; + const baseLayer: IndexPatternLayer = { + columns: { col1: baseColumn }, + columnOrder: [], + indexPatternId: '', + }; it('should keep column if there is no time scale', () => { const column = { ...baseColumn, timeScale: undefined }; - expect(adjustTimeScaleOnOtherColumnChange(column, { col1: column })).toBe(column); + expect( + adjustTimeScaleOnOtherColumnChange( + { ...baseLayer, columns: { col1: column } }, + 'col1', + 'col2' + ) + ).toBe(column); }); it('should keep time scale if there is a date histogram', () => { expect( - adjustTimeScaleOnOtherColumnChange(baseColumn, { - col1: baseColumn, - col2: { - operationType: 'date_histogram', - dataType: 'date', - isBucketed: true, - label: '', + adjustTimeScaleOnOtherColumnChange( + { + ...baseLayer, + columns: { + col1: baseColumn, + col2: { + operationType: 'date_histogram', + dataType: 'date', + isBucketed: true, + label: '', + sourceField: 'date', + params: { interval: 'auto' }, + }, + }, }, - }) + 'col1', + 'col2' + ) ).toBe(baseColumn); }); it('should remove time scale if there is no date histogram', () => { - expect(adjustTimeScaleOnOtherColumnChange(baseColumn, { col1: baseColumn })).toHaveProperty( + expect(adjustTimeScaleOnOtherColumnChange(baseLayer, 'col1', 'col2')).toHaveProperty( 'timeScale', undefined ); }); it('should remove suffix from label', () => { - expect(adjustTimeScaleOnOtherColumnChange(baseColumn, { col1: baseColumn })).toHaveProperty( - 'label', - 'Count of records' - ); + expect( + adjustTimeScaleOnOtherColumnChange( + { ...baseLayer, columns: { col1: baseColumn } }, + 'col1', + 'col2' + ) + ).toHaveProperty('label', 'Count of records'); }); it('should keep custom label', () => { const column = { ...baseColumn, label: 'abc', customLabel: true }; - expect(adjustTimeScaleOnOtherColumnChange(column, { col1: column })).toHaveProperty( - 'label', - 'abc' - ); + expect( + adjustTimeScaleOnOtherColumnChange( + { ...baseLayer, columns: { col1: column } }, + 'col1', + 'col2' + ) + ).toHaveProperty('label', 'abc'); }); }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.ts index 5d525e573a617..340cad97e7db0 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.ts @@ -5,8 +5,9 @@ */ import { unitSuffixesLong } from '../suffix_formatter'; -import { TimeScaleUnit } from '../time_scale'; -import { BaseIndexPatternColumn } from './definitions/column_types'; +import type { TimeScaleUnit } from '../time_scale'; +import type { IndexPatternLayer } from '../types'; +import type { IndexPatternColumn } from './definitions'; export const DEFAULT_TIME_SCALE = 's' as TimeScaleUnit; @@ -30,10 +31,13 @@ export function adjustTimeScaleLabelSuffix( return `${cleanedLabel} ${unitSuffixesLong[newTimeScale]}`; } -export function adjustTimeScaleOnOtherColumnChange( - column: T, - columns: Partial> -) { +export function adjustTimeScaleOnOtherColumnChange( + layer: IndexPatternLayer, + thisColumnId: string, + changedColumnId: string +): T { + const columns = layer.columns; + const column = columns[thisColumnId] as T; if (!column.timeScale) { return column; } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts b/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts index a5ce4dfbea371..38f51f24aae7d 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts @@ -20,6 +20,7 @@ import { operationDefinitionMap } from './operations'; import { IndexPattern, IndexPatternPrivateState, IndexPatternLayer } from './types'; import { OriginalColumn } from './rename_columns'; import { dateHistogramOperation } from './operations/definitions'; +import { getEsAggsSuffix } from './operations/definitions/helpers'; function getExpressionForLayer( layer: IndexPatternLayer, @@ -41,15 +42,20 @@ function getExpressionForLayer( expressions.push(...def.toExpression(layer, colId, indexPattern)); } else { aggs.push( - buildExpression({ type: 'expression', chain: [def.toEsAggsFn(col, colId, indexPattern)] }) + buildExpression({ + type: 'expression', + chain: [def.toEsAggsFn(col, colId, indexPattern, layer)], + }) ); } }); const idMap = columnEntries.reduce((currentIdMap, [colId, column], index) => { + const esAggsId = `col-${columnEntries.length === 1 ? 0 : index}-${colId}`; + const suffix = getEsAggsSuffix(column); return { ...currentIdMap, - [`col-${columnEntries.length === 1 ? 0 : index}-${colId}`]: { + [`${esAggsId}${suffix}`]: { ...column, id: colId, }, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/utils.ts b/x-pack/plugins/lens/public/indexpattern_datasource/utils.ts index 702930d02a90e..57cc4abeb723a 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/utils.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/utils.ts @@ -5,7 +5,7 @@ */ import { DataType } from '../types'; -import { IndexPatternPrivateState, IndexPattern, IndexPatternLayer } from './types'; +import { IndexPattern, IndexPatternLayer } from './types'; import { DraggedField } from './indexpattern'; import { BaseIndexPatternColumn, @@ -44,29 +44,6 @@ export function isDraggedField(fieldCandidate: unknown): fieldCandidate is Dragg ); } -export function hasInvalidColumns(state: IndexPatternPrivateState) { - return getInvalidLayers(state).length > 0; -} - -export function getInvalidLayers(state: IndexPatternPrivateState) { - return Object.values(state.layers).filter((layer) => { - return layer.columnOrder.some((columnId) => - isColumnInvalid(layer, columnId, state.indexPatterns[layer.indexPatternId]) - ); - }); -} - -export function getInvalidColumnsForLayer( - layers: IndexPatternLayer[], - indexPatternMap: Record -) { - return layers.map((layer) => { - return layer.columnOrder.filter((columnId) => - isColumnInvalid(layer, columnId, indexPatternMap[layer.indexPatternId]) - ); - }); -} - export function isColumnInvalid( layer: IndexPatternLayer, columnId: string, diff --git a/x-pack/plugins/lens/public/pie_visualization/expression.tsx b/x-pack/plugins/lens/public/pie_visualization/expression.tsx index 5f18ef7c7f637..63261d08ff1a4 100644 --- a/x-pack/plugins/lens/public/pie_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/expression.tsx @@ -140,6 +140,7 @@ export const getPieRenderer = (dependencies: { paletteService={dependencies.paletteService} onClickValue={onClickValue} renderMode={handlers.getRenderMode()} + syncColors={handlers.isSyncColorsEnabled()} /> , domNode, diff --git a/x-pack/plugins/lens/public/pie_visualization/render_function.test.tsx b/x-pack/plugins/lens/public/pie_visualization/render_function.test.tsx index 458b1a75c4c17..c6eed36f81ab0 100644 --- a/x-pack/plugins/lens/public/pie_visualization/render_function.test.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/render_function.test.tsx @@ -71,6 +71,7 @@ describe('PieVisualization component', () => { chartsThemeService, paletteService: chartPluginMock.createPaletteRegistry(), renderMode: 'display' as const, + syncColors: false, }; } @@ -172,6 +173,7 @@ describe('PieVisualization component', () => { { maxDepth: 2, totalSeries: 5, + syncColors: false, behindText: true, }, undefined diff --git a/x-pack/plugins/lens/public/pie_visualization/render_function.tsx b/x-pack/plugins/lens/public/pie_visualization/render_function.tsx index 56ecf57f2dff7..b4c81cfb6e9c3 100644 --- a/x-pack/plugins/lens/public/pie_visualization/render_function.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/render_function.tsx @@ -38,6 +38,15 @@ import { } from '../../../../../src/plugins/charts/public'; import { LensIconChartDonut } from '../assets/chart_donut'; +declare global { + interface Window { + /** + * Flag used to enable debugState on elastic charts + */ + _echDebugStateFlag?: boolean; + } +} + const EMPTY_SLICE = Symbol('empty_slice'); export function PieComponent( @@ -47,12 +56,13 @@ export function PieComponent( paletteService: PaletteRegistry; onClickValue: (data: LensFilterEvent['data']) => void; renderMode: RenderMode; + syncColors: boolean; } ) { const [firstTable] = Object.values(props.data.tables); const formatters: Record> = {}; - const { chartsThemeService, paletteService, onClickValue } = props; + const { chartsThemeService, paletteService, syncColors, onClickValue } = props; const { shape, groups, @@ -145,6 +155,7 @@ export function PieComponent( behindText: categoryDisplay !== 'hide', maxDepth: bucketColumns.length, totalSeries: totalSeriesCount, + syncColors, }, palette.params ); @@ -249,6 +260,7 @@ export function PieComponent( >

    } diff --git a/x-pack/plugins/ml/public/application/routing/routes/explorer.tsx b/x-pack/plugins/ml/public/application/routing/routes/explorer.tsx index 15ce3847f4d9c..0075846571cee 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/explorer.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/explorer.tsx @@ -201,7 +201,7 @@ const ExplorerUrlStateManager: FC = ({ jobsWithTim const [selectedCells, setSelectedCells] = useSelectedCells( explorerUrlState, setExplorerUrlState, - explorerState?.swimlaneBucketInterval + explorerState?.swimlaneBucketInterval?.asSeconds() ); useEffect(() => { diff --git a/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer.test.tsx b/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer.test.tsx index b60a265560455..c3739a18190c7 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer.test.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer.test.tsx @@ -5,12 +5,48 @@ */ import React from 'react'; -import { MemoryRouter } from 'react-router-dom'; import { render } from '@testing-library/react'; - import { I18nProvider } from '@kbn/i18n/react'; - import { TimeSeriesExplorerUrlStateManager } from './timeseriesexplorer'; +import { TimeSeriesExplorer } from '../../timeseriesexplorer'; +import { TimeSeriesExplorerPage } from '../../timeseriesexplorer/timeseriesexplorer_page'; +import { TimeseriesexplorerNoJobsFound } from '../../timeseriesexplorer/components/timeseriesexplorer_no_jobs_found'; + +jest.mock('../../services/toast_notification_service'); + +jest.mock('../../timeseriesexplorer', () => ({ + TimeSeriesExplorer: jest.fn(() => { + return null; + }), +})); + +jest.mock('../../timeseriesexplorer/timeseriesexplorer_page', () => ({ + TimeSeriesExplorerPage: jest.fn(({ children }) => { + return <>{children}; + }), +})); + +jest.mock('../../timeseriesexplorer/components/timeseriesexplorer_no_jobs_found', () => ({ + TimeseriesexplorerNoJobsFound: jest.fn(() => { + return null; + }), +})); + +const MockedTimeSeriesExplorer = TimeSeriesExplorer as jest.MockedClass; +const MockedTimeSeriesExplorerPage = TimeSeriesExplorerPage as jest.MockedFunction< + typeof TimeSeriesExplorerPage +>; +const MockedTimeseriesexplorerNoJobsFound = TimeseriesexplorerNoJobsFound as jest.MockedFunction< + typeof TimeseriesexplorerNoJobsFound +>; + +jest.mock('../../util/url_state'); + +jest.mock('../../timeseriesexplorer/hooks/use_timeseriesexplorer_url_state'); + +jest.mock('../../components/help_menu', () => ({ + HelpMenu: () =>
    , +})); jest.mock('../../contexts/kibana/kibana_context', () => { // eslint-disable-next-line @typescript-eslint/no-var-requires @@ -53,33 +89,33 @@ jest.mock('../../contexts/kibana/kibana_context', () => { addDanger: () => {}, }, }, + docLinks: { + links: { + ml: { anomalyDetection: jest.fn() }, + }, + }, }, }; }, }; }); -jest.mock('../../util/dependency_cache', () => ({ - getToastNotifications: () => ({ addSuccess: jest.fn(), addDanger: jest.fn() }), -})); - -jest.mock('../../../../shared_imports'); - describe('TimeSeriesExplorerUrlStateManager', () => { - test('Initial render shows "No single metric jobs found"', () => { + test('should render TimeseriesexplorerNoJobsFound when no jobs provided', () => { const props = { config: { get: () => 'Browser' }, jobsWithTimeRange: [], }; - const { container } = render( + render( - - - + ); - expect(container.textContent).toContain('No single metric jobs found'); + // assert + expect(MockedTimeSeriesExplorer).not.toHaveBeenCalled(); + expect(MockedTimeSeriesExplorerPage).toHaveBeenCalled(); + expect(MockedTimeseriesexplorerNoJobsFound).toHaveBeenCalled(); }); }); diff --git a/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer.tsx b/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer.tsx index 7de59cba495af..857e894d404ae 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer.tsx @@ -11,7 +11,7 @@ import moment from 'moment'; import { i18n } from '@kbn/i18n'; -import { NavigateToPath } from '../../contexts/kibana'; +import { NavigateToPath, useNotifications } from '../../contexts/kibana'; import { MlJobWithTimeRange } from '../../../../common/types/anomaly_detection_jobs'; @@ -93,6 +93,7 @@ export const TimeSeriesExplorerUrlStateManager: FC { + const { toasts } = useNotifications(); const toastNotificationService = useToastNotificationService(); const [ timeSeriesExplorerUrlState, @@ -249,7 +250,12 @@ export const TimeSeriesExplorerUrlStateManager: FC + `; diff --git a/x-pack/plugins/ml/public/application/settings/calendars/edit/new_calendar.js b/x-pack/plugins/ml/public/application/settings/calendars/edit/new_calendar.js index a5eb212ba127e..007fba1f9c666 100644 --- a/x-pack/plugins/ml/public/application/settings/calendars/edit/new_calendar.js +++ b/x-pack/plugins/ml/public/application/settings/calendars/edit/new_calendar.js @@ -21,6 +21,8 @@ import { ml } from '../../../services/ml_api_service'; import { withKibana } from '../../../../../../../../src/plugins/kibana_react/public'; import { GLOBAL_CALENDAR } from '../../../../../common/constants/calendars'; import { ML_PAGES } from '../../../../../common/constants/ml_url_generator'; +import { getDocLinks } from '../../../util/dependency_cache'; +import { HelpMenu } from '../../../components/help_menu'; class NewCalendarUI extends Component { static propTypes = { @@ -328,6 +330,8 @@ class NewCalendarUI extends Component { isGlobalCalendar, } = this.state; + const helpLink = getDocLinks().links.ml.calendars; + let modal = ''; if (isNewEventModalVisible) { @@ -389,6 +393,7 @@ class NewCalendarUI extends Component { {modal} + ); } diff --git a/x-pack/plugins/ml/public/application/settings/calendars/edit/new_calendar.test.js b/x-pack/plugins/ml/public/application/settings/calendars/edit/new_calendar.test.js index c03a5d73125d3..ae9d152797891 100644 --- a/x-pack/plugins/ml/public/application/settings/calendars/edit/new_calendar.test.js +++ b/x-pack/plugins/ml/public/application/settings/calendars/edit/new_calendar.test.js @@ -11,6 +11,19 @@ jest.mock('../../../contexts/kibana/use_create_url', () => ({ jest.mock('../../../components/navigation_menu', () => ({ NavigationMenu: () =>
    , })); + +jest.mock('../../../components/help_menu', () => ({ + HelpMenu: () =>
    , +})); + +jest.mock('../../../util/dependency_cache', () => ({ + getDocLinks: () => ({ + links: { + ml: { calendars: jest.fn() }, + }, + }), +})); + jest.mock('../../../capabilities/check_capabilities', () => ({ checkPermission: () => true, })); diff --git a/x-pack/plugins/ml/public/application/settings/calendars/list/__snapshots__/calendars_list.test.js.snap b/x-pack/plugins/ml/public/application/settings/calendars/list/__snapshots__/calendars_list.test.js.snap index 0f7585e3a9fa6..654e0e53bc3b1 100644 --- a/x-pack/plugins/ml/public/application/settings/calendars/list/__snapshots__/calendars_list.test.js.snap +++ b/x-pack/plugins/ml/public/application/settings/calendars/list/__snapshots__/calendars_list.test.js.snap @@ -71,5 +71,8 @@ exports[`CalendarsList Renders calendar list with calendars 1`] = ` + `; diff --git a/x-pack/plugins/ml/public/application/settings/calendars/list/calendars_list.js b/x-pack/plugins/ml/public/application/settings/calendars/list/calendars_list.js index dcaaf2043c63e..f90f69195a410 100644 --- a/x-pack/plugins/ml/public/application/settings/calendars/list/calendars_list.js +++ b/x-pack/plugins/ml/public/application/settings/calendars/list/calendars_list.js @@ -25,6 +25,8 @@ import { deleteCalendars } from './delete_calendars'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { withKibana } from '../../../../../../../../src/plugins/kibana_react/public'; +import { getDocLinks } from '../../../util/dependency_cache'; +import { HelpMenu } from '../../../components/help_menu'; export class CalendarsListUI extends Component { static propTypes = { @@ -104,6 +106,8 @@ export class CalendarsListUI extends Component { const { canCreateCalendar, canDeleteCalendar } = this.props; let destroyModal = ''; + const helpLink = getDocLinks().links.ml.calendars; + if (this.state.isDestroyModalVisible) { destroyModal = ( @@ -168,6 +172,7 @@ export class CalendarsListUI extends Component { {destroyModal} + ); } diff --git a/x-pack/plugins/ml/public/application/settings/calendars/list/calendars_list.test.js b/x-pack/plugins/ml/public/application/settings/calendars/list/calendars_list.test.js index ff294645deeb6..2efa75119f10d 100644 --- a/x-pack/plugins/ml/public/application/settings/calendars/list/calendars_list.test.js +++ b/x-pack/plugins/ml/public/application/settings/calendars/list/calendars_list.test.js @@ -13,6 +13,19 @@ import { CalendarsList } from './calendars_list'; jest.mock('../../../components/navigation_menu', () => ({ NavigationMenu: () =>
    , })); + +jest.mock('../../../components/help_menu', () => ({ + HelpMenu: () =>
    , +})); + +jest.mock('../../../util/dependency_cache', () => ({ + getDocLinks: () => ({ + links: { + ml: { calendars: jest.fn() }, + }, + }), +})); + jest.mock('../../../capabilities/check_capabilities', () => ({ checkPermission: () => true, })); diff --git a/x-pack/plugins/ml/public/application/settings/filter_lists/edit/__snapshots__/edit_filter_list.test.js.snap b/x-pack/plugins/ml/public/application/settings/filter_lists/edit/__snapshots__/edit_filter_list.test.js.snap index f6a4f76975553..b99acc41c2c6a 100644 --- a/x-pack/plugins/ml/public/application/settings/filter_lists/edit/__snapshots__/edit_filter_list.test.js.snap +++ b/x-pack/plugins/ml/public/application/settings/filter_lists/edit/__snapshots__/edit_filter_list.test.js.snap @@ -110,6 +110,9 @@ exports[`EditFilterList adds new items to filter list 1`] = ` + `; @@ -225,6 +228,9 @@ exports[`EditFilterList renders after selecting an item and deleting it 1`] = ` + `; @@ -335,6 +341,9 @@ exports[`EditFilterList renders after selecting an item and deleting it 2`] = ` + `; @@ -429,6 +438,9 @@ exports[`EditFilterList renders the edit page for a new filter list and updates + `; @@ -523,6 +535,9 @@ exports[`EditFilterList renders the edit page for a new filter list and updates + `; @@ -634,6 +649,9 @@ exports[`EditFilterList renders the edit page for an existing filter list and up + `; @@ -745,6 +763,9 @@ exports[`EditFilterList renders the edit page for an existing filter list and up + `; @@ -856,5 +877,8 @@ exports[`EditFilterList updates the items per page 1`] = ` + `; diff --git a/x-pack/plugins/ml/public/application/settings/filter_lists/edit/edit_filter_list.js b/x-pack/plugins/ml/public/application/settings/filter_lists/edit/edit_filter_list.js index 9ea470a388f02..0951652135a5a 100644 --- a/x-pack/plugins/ml/public/application/settings/filter_lists/edit/edit_filter_list.js +++ b/x-pack/plugins/ml/public/application/settings/filter_lists/edit/edit_filter_list.js @@ -35,6 +35,8 @@ import { NavigationMenu } from '../../../components/navigation_menu'; import { isValidFilterListId, saveFilterList } from './utils'; import { ml } from '../../../services/ml_api_service'; import { ML_PAGES } from '../../../../../common/constants/ml_url_generator'; +import { getDocLinks } from '../../../util/dependency_cache'; +import { HelpMenu } from '../../../components/help_menu'; const DEFAULT_ITEMS_PER_PAGE = 50; @@ -320,6 +322,8 @@ export class EditFilterListUI extends Component { const totalItemCount = items !== undefined ? items.length : 0; + const helpLink = getDocLinks().links.ml.customRules; + return ( @@ -393,6 +397,7 @@ export class EditFilterListUI extends Component { + ); } diff --git a/x-pack/plugins/ml/public/application/settings/filter_lists/edit/edit_filter_list.test.js b/x-pack/plugins/ml/public/application/settings/filter_lists/edit/edit_filter_list.test.js index 148f435a9104f..28f8148d889f7 100644 --- a/x-pack/plugins/ml/public/application/settings/filter_lists/edit/edit_filter_list.test.js +++ b/x-pack/plugins/ml/public/application/settings/filter_lists/edit/edit_filter_list.test.js @@ -8,6 +8,18 @@ jest.mock('../../../components/navigation_menu', () => ({ NavigationMenu: () =>
    , })); +jest.mock('../../../components/help_menu', () => ({ + HelpMenu: () =>
    , +})); + +jest.mock('../../../util/dependency_cache', () => ({ + getDocLinks: () => ({ + links: { + ml: { customRules: jest.fn() }, + }, + }), +})); + // Define the required mocks used for loading, saving and validating the filter list. jest.mock('./utils', () => ({ isValidFilterListId: () => true, diff --git a/x-pack/plugins/ml/public/application/settings/filter_lists/list/__snapshots__/filter_lists.test.js.snap b/x-pack/plugins/ml/public/application/settings/filter_lists/list/__snapshots__/filter_lists.test.js.snap index 3a78a46369c1b..f1ba1ba02999b 100644 --- a/x-pack/plugins/ml/public/application/settings/filter_lists/list/__snapshots__/filter_lists.test.js.snap +++ b/x-pack/plugins/ml/public/application/settings/filter_lists/list/__snapshots__/filter_lists.test.js.snap @@ -43,5 +43,8 @@ exports[`Filter Lists renders a list of filters 1`] = ` + `; diff --git a/x-pack/plugins/ml/public/application/settings/filter_lists/list/filter_lists.js b/x-pack/plugins/ml/public/application/settings/filter_lists/list/filter_lists.js index 75c72fdab7307..334af47513ad9 100644 --- a/x-pack/plugins/ml/public/application/settings/filter_lists/list/filter_lists.js +++ b/x-pack/plugins/ml/public/application/settings/filter_lists/list/filter_lists.js @@ -22,6 +22,9 @@ import { FilterListsHeader } from './header'; import { FilterListsTable } from './table'; import { ml } from '../../../services/ml_api_service'; +import { getDocLinks } from '../../../util/dependency_cache'; +import { HelpMenu } from '../../../components/help_menu'; + export class FilterListsUI extends Component { static displayName = 'FilterLists'; static propTypes = { @@ -85,6 +88,7 @@ export class FilterListsUI extends Component { render() { const { filterLists, selectedFilterLists } = this.state; const { canCreateFilter, canDeleteFilter } = this.props; + const helpLink = getDocLinks().links.ml.customRules; return ( @@ -111,6 +115,7 @@ export class FilterListsUI extends Component { + ); } diff --git a/x-pack/plugins/ml/public/application/settings/filter_lists/list/filter_lists.test.js b/x-pack/plugins/ml/public/application/settings/filter_lists/list/filter_lists.test.js index 1f290f807ab45..efb53eb1dd7d2 100644 --- a/x-pack/plugins/ml/public/application/settings/filter_lists/list/filter_lists.test.js +++ b/x-pack/plugins/ml/public/application/settings/filter_lists/list/filter_lists.test.js @@ -12,6 +12,19 @@ import { FilterLists } from './filter_lists'; jest.mock('../../../components/navigation_menu', () => ({ NavigationMenu: () =>
    , })); + +jest.mock('../../../components/help_menu', () => ({ + HelpMenu: () =>
    , +})); + +jest.mock('../../../util/dependency_cache', () => ({ + getDocLinks: () => ({ + links: { + ml: { customRules: jest.fn() }, + }, + }), +})); + jest.mock('../../../capabilities/check_capabilities', () => ({ checkPermission: () => true, })); diff --git a/x-pack/plugins/ml/public/application/settings/settings.test.tsx b/x-pack/plugins/ml/public/application/settings/settings.test.tsx index 1ef216560604b..be7014ed3f70d 100644 --- a/x-pack/plugins/ml/public/application/settings/settings.test.tsx +++ b/x-pack/plugins/ml/public/application/settings/settings.test.tsx @@ -14,12 +14,27 @@ jest.mock('../components/navigation_menu', () => ({ NavigationMenu: () =>
    , })); +jest.mock('../components/help_menu', () => ({ + HelpMenu: () =>
    , +})); + jest.mock('../contexts/kibana', () => ({ useNotifications: () => { return { toasts: { addDanger: jest.fn() }, }; }, + useMlKibana: () => { + return { + services: { + docLinks: { + links: { + ml: { guide: jest.fn() }, + }, + }, + }, + }; + }, })); jest.mock('../contexts/kibana/use_create_url', () => ({ diff --git a/x-pack/plugins/ml/public/application/settings/settings.tsx b/x-pack/plugins/ml/public/application/settings/settings.tsx index 94195678dd503..f43e87e43ffc1 100644 --- a/x-pack/plugins/ml/public/application/settings/settings.tsx +++ b/x-pack/plugins/ml/public/application/settings/settings.tsx @@ -14,7 +14,14 @@ import { AnomalyDetectionSettings } from './anomaly_detection_settings'; import { NavigationMenu } from '../components/navigation_menu'; +import { HelpMenu } from '../components/help_menu'; +import { useMlKibana } from '../contexts/kibana'; + export const Settings: FC = () => { + const { + services: { docLinks }, + } = useMlKibana(); + const helpLink = docLinks.links.ml.guide; return ( @@ -32,6 +39,7 @@ export const Settings: FC = () => { + ); }; diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js index 32fa27b70989e..768de330cce1d 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js @@ -689,7 +689,7 @@ class TimeseriesChartIntl extends Component { const levels = getAnnotationLevels(focusAnnotationData); const maxLevel = d3.max(Object.keys(levels).map((key) => levels[key])); // TODO needs revisiting to be a more robust normalization - yMax = yMax * (1 + (maxLevel + 1) / 5); + yMax += Math.abs(yMax - yMin) * ((maxLevel + 1) / 5); } this.focusYScale.domain([yMin, yMax]); } else { diff --git a/x-pack/plugins/logstash/public/application/components/upgrade_failure/index.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/hooks/__mocks__/use_timeseriesexplorer_url_state.ts similarity index 72% rename from x-pack/plugins/logstash/public/application/components/upgrade_failure/index.js rename to x-pack/plugins/ml/public/application/timeseriesexplorer/hooks/__mocks__/use_timeseriesexplorer_url_state.ts index 0aa757bca5236..b44fbf5838b52 100644 --- a/x-pack/plugins/logstash/public/application/components/upgrade_failure/index.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/hooks/__mocks__/use_timeseriesexplorer_url_state.ts @@ -4,4 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export { UpgradeFailure } from './upgrade_failure'; +export const useTimeSeriesExplorerUrlState = jest.fn(() => { + return [{}, jest.fn()]; +}); diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.d.ts b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.d.ts index 8159dbb8ade06..26525505420de 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.d.ts +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.d.ts @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { FC } from 'react'; +import React from 'react'; import { TimeRangeBounds } from '../explorer/explorer_utils'; -declare const TimeSeriesExplorer: FC<{ +interface Props { appStateHandler: (action: string, payload: any) => void; autoZoomDuration: number; bounds: TimeRangeBounds; @@ -21,4 +21,7 @@ declare const TimeSeriesExplorer: FC<{ tableInterval: string; tableSeverity: number; zoom?: { from?: string; to?: string }; -}>; +} + +// eslint-disable-next-line react/prefer-stateless-function +declare class TimeSeriesExplorer extends React.Component {} diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_page.tsx b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_page.tsx index cf30aff1c6c10..b445e2bd2a547 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_page.tsx +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_page.tsx @@ -25,6 +25,9 @@ import { JobSelector } from '../components/job_selector'; import { NavigationMenu } from '../components/navigation_menu'; import { DatePickerWrapper } from '../components/navigation_menu/date_picker_wrapper'; +import { HelpMenu } from '../components/help_menu'; +import { useMlKibana } from '../contexts/kibana'; + interface TimeSeriesExplorerPageProps { dateFormatTz: string; resizeRef?: any; @@ -35,6 +38,10 @@ export const TimeSeriesExplorerPage: FC = ({ dateFormatTz, resizeRef, }) => { + const { + services: { docLinks }, + } = useMlKibana(); + const helpLink = docLinks.links.ml.anomalyDetection; return ( <> @@ -78,6 +85,7 @@ export const TimeSeriesExplorerPage: FC = ({ {children} +
    ); diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/validate_job_selection.ts b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/validate_job_selection.ts index cd8a10a9e1f99..1781d0ee6369b 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/validate_job_selection.ts +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/validate_job_selection.ts @@ -8,8 +8,7 @@ import { difference, without } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { getToastNotifications } from '../../util/dependency_cache'; - +import { ToastsStart } from 'kibana/public'; import { MlJobWithTimeRange } from '../../../../common/types/anomaly_detection_jobs'; import { getTimeRangeFromSelection } from '../../components/job_selector/job_select_service_utils'; @@ -24,9 +23,9 @@ import { createTimeSeriesJobData } from './timeseriesexplorer_utils'; export function validateJobSelection( jobsWithTimeRange: MlJobWithTimeRange[], selectedJobIds: string[], - setGlobalState: (...args: any) => void + setGlobalState: (...args: any) => void, + toastNotifications: ToastsStart ) { - const toastNotifications = getToastNotifications(); const jobs = createTimeSeriesJobData(mlJobService.jobs); const timeSeriesJobIds: string[] = jobs.map((j: any) => j.id); diff --git a/x-pack/plugins/ml/public/application/util/__mocks__/url_state.tsx b/x-pack/plugins/ml/public/application/util/__mocks__/url_state.tsx new file mode 100644 index 0000000000000..cb237b951d8dd --- /dev/null +++ b/x-pack/plugins/ml/public/application/util/__mocks__/url_state.tsx @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { AppStateKey } from '../url_state'; +import { TABLE_INTERVAL_DEFAULT } from '../../components/controls/select_interval/select_interval'; + +export const useUrlState = jest.fn((accessor: '_a' | '_g') => { + if (accessor === '_g') { + return [{ refreshInterval: { value: 0, pause: true } }, jest.fn()]; + } +}); + +export const usePageUrlState = jest.fn((pageKey: AppStateKey) => { + let state: unknown; + switch (pageKey) { + case 'timeseriesexplorer': + state = {}; + break; + case 'mlSelectInterval': + state = TABLE_INTERVAL_DEFAULT; + break; + } + return [state, jest.fn()]; +}); diff --git a/x-pack/plugins/ml/public/application/util/chart_utils.js b/x-pack/plugins/ml/public/application/util/chart_utils.js index d142d2e246659..402c922a0034f 100644 --- a/x-pack/plugins/ml/public/application/util/chart_utils.js +++ b/x-pack/plugins/ml/public/application/util/chart_utils.js @@ -264,7 +264,7 @@ export async function getExploreSeriesLink(mlUrlGenerator, series) { }, }, }, - excludeBasePath: false, + excludeBasePath: true, }); return url; } diff --git a/x-pack/plugins/ml/public/application/util/url_state.tsx b/x-pack/plugins/ml/public/application/util/url_state.tsx index 6cdc069096dcc..b565a0f7b7a73 100644 --- a/x-pack/plugins/ml/public/application/util/url_state.tsx +++ b/x-pack/plugins/ml/public/application/util/url_state.tsx @@ -73,7 +73,9 @@ export const urlStateStore = createContext({ searchString: '', setUrlState: () => {}, }); + const { Provider } = urlStateStore; + export const UrlStateProvider: FC = ({ children }) => { const history = useHistory(); const { search: searchString } = useLocation(); @@ -162,7 +164,14 @@ export const useUrlState = (accessor: Accessor) => { return [urlState, setUrlState]; }; -type AppStateKey = 'mlSelectSeverity' | 'mlSelectInterval' | 'mlAnomaliesTable' | MlPages; +type LegacyUrlKeys = 'mlExplorerSwimlane'; + +export type AppStateKey = + | 'mlSelectSeverity' + | 'mlSelectInterval' + | 'mlAnomaliesTable' + | MlPages + | LegacyUrlKeys; /** * Hook for managing the URL state of the page. diff --git a/x-pack/plugins/ml/public/ml_url_generator/anomaly_detection_urls_generator.ts b/x-pack/plugins/ml/public/ml_url_generator/anomaly_detection_urls_generator.ts index d2814bd63b0b0..b6a3ca0ce7139 100644 --- a/x-pack/plugins/ml/public/ml_url_generator/anomaly_detection_urls_generator.ts +++ b/x-pack/plugins/ml/public/ml_url_generator/anomaly_detection_urls_generator.ts @@ -163,6 +163,7 @@ export function createSingleMetricViewerUrl( entities, globalState, functionDescription, + zoom, } = params; let queryState: Partial = {}; @@ -193,6 +194,10 @@ export function createSingleMetricViewerUrl( mlTimeSeriesExplorer.functionDescription = functionDescription; } + if (zoom !== undefined) { + mlTimeSeriesExplorer.zoom = zoom; + } + appState.mlTimeSeriesExplorer = mlTimeSeriesExplorer; if (query) diff --git a/x-pack/plugins/ml/public/ml_url_generator/ml_url_generator.test.ts b/x-pack/plugins/ml/public/ml_url_generator/ml_url_generator.test.ts index 7dcd901c2c0ef..21da0424cdca0 100644 --- a/x-pack/plugins/ml/public/ml_url_generator/ml_url_generator.test.ts +++ b/x-pack/plugins/ml/public/ml_url_generator/ml_url_generator.test.ts @@ -161,7 +161,7 @@ describe('MlUrlGenerator', () => { }, }); expect(url).toBe( - "/app/ml/timeseriesexplorer?_g=(ml:(jobIds:!(logs_categorization_1)),refreshInterval:(pause:!f,value:0),time:(from:'2020-07-12T00:39:02.912Z',mode:absolute,to:'2020-07-22T15:52:18.613Z'))&_a=(timeseriesexplorer:(mlTimeSeriesExplorer:(detectorIndex:0,entities:(mlcategory:'2')),query:(query_string:(analyze_wildcard:!t,query:'*'))))" + "/app/ml/timeseriesexplorer?_g=(ml:(jobIds:!(logs_categorization_1)),refreshInterval:(pause:!f,value:0),time:(from:'2020-07-12T00:39:02.912Z',mode:absolute,to:'2020-07-22T15:52:18.613Z'))&_a=(timeseriesexplorer:(mlTimeSeriesExplorer:(detectorIndex:0,entities:(mlcategory:'2'),zoom:(from:'2020-07-20T23:58:29.367Z',to:'2020-07-21T11:00:13.173Z')),query:(query_string:(analyze_wildcard:!t,query:'*'))))" ); }); }); diff --git a/x-pack/plugins/ml/server/models/job_service/groups.ts b/x-pack/plugins/ml/server/models/job_service/groups.ts index 59090f30ccca9..f6073ae7071b0 100644 --- a/x-pack/plugins/ml/server/models/job_service/groups.ts +++ b/x-pack/plugins/ml/server/models/job_service/groups.ts @@ -72,7 +72,9 @@ export function groupsProvider(mlClient: MlClient) { }); } - return Object.keys(groups).map((g) => groups[g]); + return Object.keys(groups) + .sort() + .map((g) => groups[g]); } async function updateGroups(jobs: Job[]) { diff --git a/x-pack/plugins/monitoring/common/types/alerts.ts b/x-pack/plugins/monitoring/common/types/alerts.ts index 93807f9df12b0..df6e169f37f7a 100644 --- a/x-pack/plugins/monitoring/common/types/alerts.ts +++ b/x-pack/plugins/monitoring/common/types/alerts.ts @@ -4,14 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Alert, SanitizedAlert } from '../../../alerts/common'; +import { Alert, AlertTypeParams, SanitizedAlert } from '../../../alerts/common'; import { AlertParamType, AlertMessageTokenType, AlertSeverity } from '../enums'; -export type CommonAlert = Alert | SanitizedAlert; +export type CommonAlert = Alert | SanitizedAlert; export interface CommonAlertStatus { states: CommonAlertState[]; - rawAlert: Alert | SanitizedAlert; + rawAlert: Alert | SanitizedAlert; } export interface CommonAlertState { diff --git a/x-pack/plugins/monitoring/common/types/es.ts b/x-pack/plugins/monitoring/common/types/es.ts new file mode 100644 index 0000000000000..853e140ec66c7 --- /dev/null +++ b/x-pack/plugins/monitoring/common/types/es.ts @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export interface ElasticsearchSourceKibanaStats { + timestamp?: string; + kibana?: { + name?: string; + status?: string; + uuid?: string; + response_times?: { + max?: number; + }; + }; + os?: { + memory?: { + free_in_bytes?: number; + }; + }; + process?: { + uptime_in_millis?: number; + }; +} + +export interface ElasticsearchSource { + timestamp: string; + kibana_stats?: ElasticsearchSourceKibanaStats; + beats_stats?: { + timestamp?: string; + beat?: { + uuid?: string; + name?: string; + type?: string; + version?: string; + host?: string; + }; + metrics?: { + beat?: { + memstats?: { + memory_alloc?: number; + }; + info?: { + uptime?: { + ms?: number; + }; + }; + handles?: { + limit?: { + hard?: number; + soft?: number; + }; + }; + }; + libbeat?: { + config?: { + reloads?: number; + }; + output?: { + type?: string; + write?: { + bytes?: number; + errors?: number; + }; + read?: { + errors?: number; + }; + }; + pipeline?: { + events?: { + total?: number; + published?: number; + dropped?: number; + }; + }; + }; + }; + }; +} diff --git a/x-pack/plugins/monitoring/public/alerts/ccr_read_exceptions_alert/index.tsx b/x-pack/plugins/monitoring/public/alerts/ccr_read_exceptions_alert/index.tsx index 4d22d422ecda6..6d7751d91b761 100644 --- a/x-pack/plugins/monitoring/public/alerts/ccr_read_exceptions_alert/index.tsx +++ b/x-pack/plugins/monitoring/public/alerts/ccr_read_exceptions_alert/index.tsx @@ -9,8 +9,9 @@ import { i18n } from '@kbn/i18n'; import { Expression, Props } from '../components/duration/expression'; import { AlertTypeModel, ValidationResult } from '../../../../triggers_actions_ui/public'; import { ALERT_CCR_READ_EXCEPTIONS, ALERT_DETAILS } from '../../../common/constants'; +import { AlertTypeParams } from '../../../../alerts/common'; -interface ValidateOptions { +interface ValidateOptions extends AlertTypeParams { duration: string; } @@ -30,7 +31,7 @@ const validate = (inputValues: ValidateOptions): ValidationResult => { return validationResult; }; -export function createCCRReadExceptionsAlertType(): AlertTypeModel { +export function createCCRReadExceptionsAlertType(): AlertTypeModel { return { id: ALERT_CCR_READ_EXCEPTIONS, description: ALERT_DETAILS[ALERT_CCR_READ_EXCEPTIONS].description, diff --git a/x-pack/plugins/monitoring/public/alerts/components/duration/validation.tsx b/x-pack/plugins/monitoring/public/alerts/components/duration/validation.tsx index 892ee0926ca38..c4e5ff343da17 100644 --- a/x-pack/plugins/monitoring/public/alerts/components/duration/validation.tsx +++ b/x-pack/plugins/monitoring/public/alerts/components/duration/validation.tsx @@ -5,9 +5,11 @@ */ import { i18n } from '@kbn/i18n'; +import { AlertTypeParams } from '../../../../../alerts/common'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { ValidationResult } from '../../../../../triggers_actions_ui/public/types'; +export type MonitoringAlertTypeParams = ValidateOptions & AlertTypeParams; interface ValidateOptions { duration: string; threshold: number; diff --git a/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/cpu_usage_alert.tsx b/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/cpu_usage_alert.tsx index 1fe40fc8777f4..d2cec006b1b1d 100644 --- a/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/cpu_usage_alert.tsx +++ b/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/cpu_usage_alert.tsx @@ -7,10 +7,10 @@ import React from 'react'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types'; import { ALERT_CPU_USAGE, ALERT_DETAILS } from '../../../common/constants'; -import { validate } from '../components/duration/validation'; +import { validate, MonitoringAlertTypeParams } from '../components/duration/validation'; import { Expression, Props } from '../components/duration/expression'; -export function createCpuUsageAlertType(): AlertTypeModel { +export function createCpuUsageAlertType(): AlertTypeModel { return { id: ALERT_CPU_USAGE, description: ALERT_DETAILS[ALERT_CPU_USAGE].description, diff --git a/x-pack/plugins/monitoring/public/alerts/disk_usage_alert/index.tsx b/x-pack/plugins/monitoring/public/alerts/disk_usage_alert/index.tsx index 5579b8e1275a3..bea399ee89f6a 100644 --- a/x-pack/plugins/monitoring/public/alerts/disk_usage_alert/index.tsx +++ b/x-pack/plugins/monitoring/public/alerts/disk_usage_alert/index.tsx @@ -5,14 +5,14 @@ */ import React from 'react'; -import { validate } from '../components/duration/validation'; +import { validate, MonitoringAlertTypeParams } from '../components/duration/validation'; import { Expression, Props } from '../components/duration/expression'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types'; import { ALERT_DISK_USAGE, ALERT_DETAILS } from '../../../common/constants'; -export function createDiskUsageAlertType(): AlertTypeModel { +export function createDiskUsageAlertType(): AlertTypeModel { return { id: ALERT_DISK_USAGE, description: ALERT_DETAILS[ALERT_DISK_USAGE].description, diff --git a/x-pack/plugins/monitoring/public/alerts/memory_usage_alert/index.tsx b/x-pack/plugins/monitoring/public/alerts/memory_usage_alert/index.tsx index 0400810a8c379..0428e4e7c733e 100644 --- a/x-pack/plugins/monitoring/public/alerts/memory_usage_alert/index.tsx +++ b/x-pack/plugins/monitoring/public/alerts/memory_usage_alert/index.tsx @@ -5,14 +5,14 @@ */ import React from 'react'; -import { validate } from '../components/duration/validation'; +import { validate, MonitoringAlertTypeParams } from '../components/duration/validation'; import { Expression, Props } from '../components/duration/expression'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types'; import { ALERT_MEMORY_USAGE, ALERT_DETAILS } from '../../../common/constants'; -export function createMemoryUsageAlertType(): AlertTypeModel { +export function createMemoryUsageAlertType(): AlertTypeModel { return { id: ALERT_MEMORY_USAGE, description: ALERT_DETAILS[ALERT_MEMORY_USAGE].description, diff --git a/x-pack/plugins/monitoring/public/alerts/status.tsx b/x-pack/plugins/monitoring/public/alerts/status.tsx index 4d51069efb972..f67f1df11cfe1 100644 --- a/x-pack/plugins/monitoring/public/alerts/status.tsx +++ b/x-pack/plugins/monitoring/public/alerts/status.tsx @@ -16,8 +16,8 @@ import { SetupModeContext } from '../components/setup_mode/setup_mode_context'; interface Props { alerts: { [alertTypeId: string]: CommonAlertStatus }; showBadge: boolean; - showOnlyCount: boolean; - stateFilter: (state: AlertState) => boolean; + showOnlyCount?: boolean; + stateFilter?: (state: AlertState) => boolean; } export const AlertsStatus: React.FC = (props: Props) => { const { alerts, showBadge = false, showOnlyCount = false, stateFilter = () => true } = props; diff --git a/x-pack/plugins/monitoring/public/components/kibana/instances/instances.js b/x-pack/plugins/monitoring/public/components/kibana/instances/instances.js deleted file mode 100644 index cde7952aa1839..0000000000000 --- a/x-pack/plugins/monitoring/public/components/kibana/instances/instances.js +++ /dev/null @@ -1,294 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { PureComponent, Fragment } from 'react'; -import { - EuiPage, - EuiPageBody, - EuiPageContent, - EuiPanel, - EuiSpacer, - EuiLink, - EuiCallOut, - EuiScreenReaderOnly, - EuiToolTip, - EuiHealth, -} from '@elastic/eui'; -import { capitalize, get } from 'lodash'; -import { ClusterStatus } from '../cluster_status'; -import { EuiMonitoringTable } from '../../table'; -import { StatusIcon } from '../../status_icon'; -import { formatMetric, formatNumber } from '../../../lib/format_number'; -import { getSafeForExternalLink } from '../../../lib/get_safe_for_external_link'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { SetupModeBadge } from '../../setup_mode/badge'; -import { KIBANA_SYSTEM_ID } from '../../../../common/constants'; -import { ListingCallOut } from '../../setup_mode/listing_callout'; -import { AlertsStatus } from '../../../alerts/status'; -import { isSetupModeFeatureEnabled } from '../../../lib/setup_mode'; -import { SetupModeFeature } from '../../../../common/enums'; - -const getColumns = (setupMode, alerts) => { - const columns = [ - { - name: i18n.translate('xpack.monitoring.kibana.listing.nameColumnTitle', { - defaultMessage: 'Name', - }), - field: 'name', - render: (name, kibana) => { - let setupModeStatus = null; - if (isSetupModeFeatureEnabled(SetupModeFeature.MetricbeatMigration)) { - const list = get(setupMode, 'data.byUuid', {}); - const uuid = get(kibana, 'kibana.uuid'); - const status = list[uuid] || {}; - const instance = { - uuid, - name: kibana.name, - }; - - setupModeStatus = ( -
    - -
    - ); - if (status.isNetNewUser) { - return ( -
    - {name} - {setupModeStatus} -
    - ); - } - } - - return ( -
    - - {name} - - {setupModeStatus} -
    - ); - }, - }, - { - name: i18n.translate('xpack.monitoring.kibana.listing.alertsColumnTitle', { - defaultMessage: 'Alerts', - }), - field: 'isOnline', - width: '175px', - sortable: true, - render: () => , - }, - { - name: i18n.translate('xpack.monitoring.kibana.listing.statusColumnTitle', { - defaultMessage: 'Status', - }), - field: 'status', - render: (status, kibana) => { - return ( - - - {capitalize(status)} - - - ); - }, - }, - { - name: i18n.translate('xpack.monitoring.kibana.listing.loadAverageColumnTitle', { - defaultMessage: 'Load Average', - }), - field: 'os.load.1m', - render: (value) => {formatMetric(value, '0.00')}, - }, - { - name: i18n.translate('xpack.monitoring.kibana.listing.memorySizeColumnTitle', { - defaultMessage: 'Memory Size', - }), - field: 'process.memory.resident_set_size_in_bytes', - render: (value) => {formatNumber(value, 'byte')}, - }, - { - name: i18n.translate('xpack.monitoring.kibana.listing.requestsColumnTitle', { - defaultMessage: 'Requests', - }), - field: 'requests.total', - render: (value) => {formatNumber(value, 'int_commas')}, - }, - { - name: i18n.translate('xpack.monitoring.kibana.listing.responseTimeColumnTitle', { - defaultMessage: 'Response Times', - }), - // It is possible this does not exist through MB collection - field: 'response_times.average', - render: (value, kibana) => { - if (!value) { - return null; - } - - return ( -
    -
    - {formatNumber(value, 'int_commas') + ' ms avg'} -
    -
    - {formatNumber(kibana.response_times.max, 'int_commas')} ms max -
    -
    - ); - }, - }, - ]; - - return columns; -}; - -export class KibanaInstances extends PureComponent { - render() { - const { clusterStatus, alerts, setupMode, sorting, pagination, onTableChange } = this.props; - - let setupModeCallOut = null; - // Merge the instances data with the setup data if enabled - const instances = this.props.instances || []; - if (isSetupModeFeatureEnabled(SetupModeFeature.MetricbeatMigration)) { - // We want to create a seamless experience for the user by merging in the setup data - // and the node data from monitoring indices in the likely scenario where some instances - // are using MB collection and some are using no collection - const instancesByUuid = instances.reduce( - (byUuid, instance) => ({ - ...byUuid, - [get(instance, 'kibana.uuid')]: instance, - }), - {} - ); - - instances.push( - ...Object.entries(setupMode.data.byUuid).reduce((instances, [nodeUuid, instance]) => { - if (!instancesByUuid[nodeUuid]) { - instances.push({ - kibana: { - ...instance.instance.kibana, - status: StatusIcon.TYPES.GRAY, - }, - }); - } - return instances; - }, []) - ); - - setupModeCallOut = ( - { - const customRenderResponse = { - shouldRender: false, - componentToRender: null, - }; - - const hasInstances = setupMode.data.totalUniqueInstanceCount > 0; - if (!hasInstances) { - customRenderResponse.shouldRender = true; - customRenderResponse.componentToRender = ( - - -

    - {i18n.translate( - 'xpack.monitoring.kibana.instances.metricbeatMigration.detectedNodeDescription', - { - defaultMessage: `The following instances are not monitored. - Click 'Monitor with Metricbeat' below to start monitoring.`, - } - )} -

    -
    - -
    - ); - } - - return customRenderResponse; - }} - /> - ); - } - - const dataFlattened = instances.map((item) => ({ - ...item, - name: item.kibana.name, - status: item.kibana.status, - })); - - return ( - - - -

    - -

    -
    - - - - - {setupModeCallOut} - - - -
    -
    - ); - } -} diff --git a/x-pack/plugins/monitoring/public/components/kibana/instances/instances.tsx b/x-pack/plugins/monitoring/public/components/kibana/instances/instances.tsx new file mode 100644 index 0000000000000..102f80b4722d4 --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/kibana/instances/instances.tsx @@ -0,0 +1,314 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { Fragment } from 'react'; +import { + EuiPage, + EuiPageBody, + EuiPageContent, + EuiPanel, + EuiSpacer, + EuiLink, + EuiCallOut, + EuiScreenReaderOnly, + EuiToolTip, + EuiHealth, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { capitalize, get } from 'lodash'; +// @ts-ignore +import { ClusterStatus } from '../cluster_status'; +// @ts-ignore +import { EuiMonitoringTable } from '../../table'; +// @ts-ignore +import { StatusIcon } from '../../status_icon'; +// @ts-ignore +import { formatMetric, formatNumber } from '../../../lib/format_number'; +import { getSafeForExternalLink } from '../../../lib/get_safe_for_external_link'; +// @ts-ignore +import { SetupModeBadge } from '../../setup_mode/badge'; +import { KIBANA_SYSTEM_ID } from '../../../../common/constants'; +import { CommonAlertStatus } from '../../../../common/types/alerts'; +import { ElasticsearchSourceKibanaStats } from '../../../../common/types/es'; +// @ts-ignore +import { ListingCallOut } from '../../setup_mode/listing_callout'; +import { AlertsStatus } from '../../../alerts/status'; +import { isSetupModeFeatureEnabled } from '../../../lib/setup_mode'; +import { SetupModeFeature } from '../../../../common/enums'; + +const getColumns = (setupMode: any, alerts: { [alertTypeId: string]: CommonAlertStatus }) => { + const columns = [ + { + name: i18n.translate('xpack.monitoring.kibana.listing.nameColumnTitle', { + defaultMessage: 'Name', + }), + field: 'name', + render: (name: string, kibana: any) => { + let setupModeStatus = null; + if (isSetupModeFeatureEnabled(SetupModeFeature.MetricbeatMigration)) { + const list = get(setupMode, 'data.byUuid', {}); + const uuid = get(kibana, 'kibana.uuid'); + const status = list[uuid] || {}; + const instance = { + uuid, + name: kibana.name, + }; + + setupModeStatus = ( +
    + +
    + ); + if (status.isNetNewUser) { + return ( +
    + {name} + {setupModeStatus} +
    + ); + } + } + + return ( +
    + + {name} + + {setupModeStatus} +
    + ); + }, + }, + { + name: i18n.translate('xpack.monitoring.kibana.listing.alertsColumnTitle', { + defaultMessage: 'Alerts', + }), + field: 'isOnline', + width: '175px', + sortable: true, + render: () => , + }, + { + name: i18n.translate('xpack.monitoring.kibana.listing.statusColumnTitle', { + defaultMessage: 'Status', + }), + field: 'status', + render: ( + status: string, + kibana: Pick & { availability: boolean } + ) => { + return ( + + + {capitalize(status)} + + + ); + }, + }, + { + name: i18n.translate('xpack.monitoring.kibana.listing.loadAverageColumnTitle', { + defaultMessage: 'Load Average', + }), + field: 'os.load.1m', + render: (value: string) => {formatMetric(value, '0.00')}, + }, + { + name: i18n.translate('xpack.monitoring.kibana.listing.memorySizeColumnTitle', { + defaultMessage: 'Memory Size', + }), + field: 'process.memory.resident_set_size_in_bytes', + render: (value: string) => {formatNumber(value, 'byte')}, + }, + { + name: i18n.translate('xpack.monitoring.kibana.listing.requestsColumnTitle', { + defaultMessage: 'Requests', + }), + field: 'requests.total', + render: (value: string) => {formatNumber(value, 'int_commas')}, + }, + { + name: i18n.translate('xpack.monitoring.kibana.listing.responseTimeColumnTitle', { + defaultMessage: 'Response Times', + }), + // It is possible this does not exist through MB collection + field: 'response_times.average', + render: (value: string, kibana: ElasticsearchSourceKibanaStats['kibana']) => { + if (!value) { + return null; + } + + return ( +
    +
    + {formatNumber(value, 'int_commas') + ' ms avg'} +
    +
    + {formatNumber(kibana?.response_times?.max, 'int_commas')} ms max +
    +
    + ); + }, + }, + ]; + + return columns; +}; + +interface Props { + clusterStatus: any; + alerts: { [alertTypeId: string]: CommonAlertStatus }; + setupMode: any; + sorting: any; + pagination: any; + onTableChange: any; + instances: ElasticsearchSourceKibanaStats[]; +} + +export const KibanaInstances: React.FC = (props: Props) => { + const { clusterStatus, alerts, setupMode, sorting, pagination, onTableChange } = props; + + let setupModeCallOut = null; + // Merge the instances data with the setup data if enabled + const instances = props.instances || []; + if (isSetupModeFeatureEnabled(SetupModeFeature.MetricbeatMigration)) { + // We want to create a seamless experience for the user by merging in the setup data + // and the node data from monitoring indices in the likely scenario where some instances + // are using MB collection and some are using no collection + const instancesByUuid = instances.reduce( + (byUuid: { [uuid: string]: ElasticsearchSourceKibanaStats }, instance) => ({ + ...byUuid, + [instance.kibana?.uuid ?? '']: instance, + }), + {} + ); + + instances.push( + ...Object.entries(setupMode.data.byUuid).reduce((_instances: any, [nodeUuid, instance]) => { + if (!instancesByUuid[nodeUuid]) { + _instances.push({ + kibana: { + ...(instance as any).instance.kibana, + status: StatusIcon.TYPES.GRAY, + }, + }); + } + return _instances; + }, []) + ); + + setupModeCallOut = ( + { + const customRenderResponse = { + shouldRender: false, + componentToRender: null, + }; + + const hasInstances = setupMode.data.totalUniqueInstanceCount > 0; + if (!hasInstances) { + customRenderResponse.shouldRender = true; + // @ts-ignore + customRenderResponse.componentToRender = ( + + +

    + {i18n.translate( + 'xpack.monitoring.kibana.instances.metricbeatMigration.detectedNodeDescription', + { + defaultMessage: `The following instances are not monitored. + Click 'Monitor with Metricbeat' below to start monitoring.`, + } + )} +

    +
    + +
    + ); + } + + return customRenderResponse; + }} + /> + ); + } + + const dataFlattened = instances.map((item) => ({ + ...item, + name: item.kibana?.name, + status: item.kibana?.status, + })); + + return ( + + + +

    + +

    +
    + + + + + {setupModeCallOut} + + + +
    +
    + ); +}; diff --git a/x-pack/plugins/monitoring/public/components/no_data/__tests__/__snapshots__/no_data.test.js.snap b/x-pack/plugins/monitoring/public/components/no_data/__tests__/__snapshots__/no_data.test.js.snap index ffb1620b60fa3..8f820252d5b6b 100644 --- a/x-pack/plugins/monitoring/public/components/no_data/__tests__/__snapshots__/no_data.test.js.snap +++ b/x-pack/plugins/monitoring/public/components/no_data/__tests__/__snapshots__/no_data.test.js.snap @@ -2,7 +2,7 @@ exports[`NoData should show a default message if reason is unknown 1`] = `

    this.data, - (data) => { - const { pagination, sorting, onTableChange } = this; + () => this.renderComponent() + ); + } + + renderComponent() { + const { pagination, sorting, onTableChange } = this; - const component = ( - ( - - {flyoutComponent} - - {bottomBarComponent} - - )} - /> - ); - this.renderReact(component); - } + const component = ( + ( + + {flyoutComponent} + + {bottomBarComponent} + + )} + /> ); + this.renderReact(component); } }, }); diff --git a/x-pack/plugins/monitoring/public/views/base_eui_table_controller.js b/x-pack/plugins/monitoring/public/views/base_eui_table_controller.js index 36e36de974342..ee72d97e26059 100644 --- a/x-pack/plugins/monitoring/public/views/base_eui_table_controller.js +++ b/x-pack/plugins/monitoring/public/views/base_eui_table_controller.js @@ -61,12 +61,17 @@ export class MonitoringViewBaseEuiTableController extends MonitoringViewBaseCont this.setSorting(sort); this.onTableChange = ({ page, sort }) => { + this.setPagination(page); + this.setSorting({ sort }); setLocalStorageData(storage, { page, sort: { sort, }, }); + if (this.onTableChangeRender) { + this.onTableChangeRender(); + } }; // For pages where we do not fetch immediately, we want to fetch after pagination is applied diff --git a/x-pack/plugins/monitoring/public/views/beats/listing/index.js b/x-pack/plugins/monitoring/public/views/beats/listing/index.js index 3fedaa8161b52..6e294a53a6358 100644 --- a/x-pack/plugins/monitoring/public/views/beats/listing/index.js +++ b/x-pack/plugins/monitoring/public/views/beats/listing/index.js @@ -52,6 +52,7 @@ uiRoutes.when('/beats/beats', { this.data = $route.current.locals.pageData; this.scope = $scope; this.injector = $injector; + this.onTableChangeRender = this.renderComponent; $scope.$watch( () => this.data, diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/indices/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/indices/index.js index 490bd02db42b7..3d32ffad2c3d0 100644 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/indices/index.js +++ b/x-pack/plugins/monitoring/public/views/elasticsearch/indices/index.js @@ -64,25 +64,30 @@ uiRoutes.when('/elasticsearch/indices', { this.updateData(); }; + const renderComponent = () => { + const { clusterStatus, indices } = this.data; + this.renderReact( + + ); + }; + + this.onTableChangeRender = renderComponent; + $scope.$watch( () => this.data, (data) => { if (!data) { return; } - - const { clusterStatus, indices } = data; - this.renderReact( - - ); + renderComponent(); } ); } diff --git a/x-pack/plugins/monitoring/public/views/kibana/instances/index.js b/x-pack/plugins/monitoring/public/views/kibana/instances/index.js index 8a14801c8bd65..f31145788c49a 100644 --- a/x-pack/plugins/monitoring/public/views/kibana/instances/index.js +++ b/x-pack/plugins/monitoring/public/views/kibana/instances/index.js @@ -77,6 +77,8 @@ uiRoutes.when('/kibana/instances', { ); }; + this.onTableChangeRender = renderReact; + $scope.$watch( () => this.data, (data) => { diff --git a/x-pack/plugins/monitoring/public/views/logstash/nodes/index.js b/x-pack/plugins/monitoring/public/views/logstash/nodes/index.js index 3f3270923a389..f4ac64d1bad91 100644 --- a/x-pack/plugins/monitoring/public/views/logstash/nodes/index.js +++ b/x-pack/plugins/monitoring/public/views/logstash/nodes/index.js @@ -51,32 +51,36 @@ uiRoutes.when('/logstash/nodes', { }, }); + const renderComponent = () => { + this.renderReact( + ( + + {flyoutComponent} + + {bottomBarComponent} + + )} + /> + ); + }; + + this.onTableChangeRender = renderComponent; + $scope.$watch( () => this.data, - (data) => { - this.renderReact( - ( - - {flyoutComponent} - - {bottomBarComponent} - - )} - /> - ); - } + () => renderComponent() ); } }, diff --git a/x-pack/plugins/monitoring/server/alerts/base_alert.ts b/x-pack/plugins/monitoring/server/alerts/base_alert.ts index a3bcc310b8084..46adfebfd17bf 100644 --- a/x-pack/plugins/monitoring/server/alerts/base_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/base_alert.ts @@ -13,7 +13,7 @@ import { AlertsClient, AlertServices, } from '../../../alerts/server'; -import { Alert, RawAlertInstance, SanitizedAlert } from '../../../alerts/common'; +import { Alert, AlertTypeParams, RawAlertInstance, SanitizedAlert } from '../../../alerts/common'; import { ActionsClient } from '../../../actions/server'; import { AlertState, @@ -135,7 +135,7 @@ export class BaseAlert { alertsClient: AlertsClient, actionsClient: ActionsClient, actions: AlertEnableAction[] - ): Promise { + ): Promise> { const existingAlertData = await alertsClient.find({ options: { search: this.alertOptions.id, @@ -170,7 +170,7 @@ export class BaseAlert { throttle = '1d', interval = '1m', } = this.alertOptions; - return await alertsClient.create({ + return await alertsClient.create({ data: { enabled: true, tags: [], diff --git a/x-pack/plugins/monitoring/server/lib/kibana/get_kibana_info.js b/x-pack/plugins/monitoring/server/lib/kibana/get_kibana_info.ts similarity index 68% rename from x-pack/plugins/monitoring/server/lib/kibana/get_kibana_info.js rename to x-pack/plugins/monitoring/server/lib/kibana/get_kibana_info.ts index 5a3e2dea930e0..0e8903908a55e 100644 --- a/x-pack/plugins/monitoring/server/lib/kibana/get_kibana_info.js +++ b/x-pack/plugins/monitoring/server/lib/kibana/get_kibana_info.ts @@ -4,21 +4,28 @@ * you may not use this file except in compliance with the Elastic License. */ -import { get, merge } from 'lodash'; +import { merge } from 'lodash'; +// @ts-ignore import { checkParam } from '../error_missing_required'; +// @ts-ignore import { calculateAvailability } from '../calculate_availability'; +import { LegacyRequest, ElasticsearchResponse } from '../../types'; -export function handleResponse(resp) { - const source = get(resp, 'hits.hits[0]._source.kibana_stats'); - const kibana = get(source, 'kibana'); +export function handleResponse(resp: ElasticsearchResponse) { + const source = resp.hits?.hits[0]._source.kibana_stats; + const kibana = source?.kibana; return merge(kibana, { - availability: calculateAvailability(get(source, 'timestamp')), - os_memory_free: get(source, 'os.memory.free_in_bytes'), - uptime: get(source, 'process.uptime_in_millis'), + availability: calculateAvailability(source?.timestamp), + os_memory_free: source?.os?.memory?.free_in_bytes, + uptime: source?.process?.uptime_in_millis, }); } -export function getKibanaInfo(req, kbnIndexPattern, { clusterUuid, kibanaUuid }) { +export function getKibanaInfo( + req: LegacyRequest, + kbnIndexPattern: string, + { clusterUuid, kibanaUuid }: { clusterUuid: string; kibanaUuid: string } +) { checkParam(kbnIndexPattern, 'kbnIndexPattern in getKibanaInfo'); const params = { diff --git a/x-pack/plugins/monitoring/server/plugin.ts b/x-pack/plugins/monitoring/server/plugin.ts index 22ea6c31dbe69..9af95019dafd8 100644 --- a/x-pack/plugins/monitoring/server/plugin.ts +++ b/x-pack/plugins/monitoring/server/plugin.ts @@ -335,6 +335,7 @@ export class Plugin { } }, server: { + route: () => {}, config: legacyConfigWrapper, newPlatform: { setup: { diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/kibana/instance.js b/x-pack/plugins/monitoring/server/routes/api/v1/kibana/instance.ts similarity index 89% rename from x-pack/plugins/monitoring/server/routes/api/v1/kibana/instance.js rename to x-pack/plugins/monitoring/server/routes/api/v1/kibana/instance.ts index 02229de372862..845908e6df341 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/kibana/instance.js +++ b/x-pack/plugins/monitoring/server/routes/api/v1/kibana/instance.ts @@ -6,11 +6,16 @@ import { schema } from '@kbn/config-schema'; import { getKibanaInfo } from '../../../../lib/kibana/get_kibana_info'; +// @ts-ignore import { handleError } from '../../../../lib/errors'; +// @ts-ignore import { getMetrics } from '../../../../lib/details/get_metrics'; +// @ts-ignore import { prefixIndexPattern } from '../../../../lib/ccs_utils'; +// @ts-ignore import { metricSet } from './metric_set_instance'; import { INDEX_PATTERN_KIBANA } from '../../../../../common/constants'; +import { LegacyRequest, LegacyServer } from '../../../../types'; /** * Kibana instance: This will fetch all data required to display a Kibana @@ -18,7 +23,7 @@ import { INDEX_PATTERN_KIBANA } from '../../../../../common/constants'; * - Kibana Instance Summary (Status) * - Metrics */ -export function kibanaInstanceRoute(server) { +export function kibanaInstanceRoute(server: LegacyServer) { server.route({ method: 'POST', path: '/api/monitoring/v1/clusters/{clusterUuid}/kibana/{kibanaUuid}', @@ -37,7 +42,7 @@ export function kibanaInstanceRoute(server) { }), }, }, - async handler(req) { + async handler(req: LegacyRequest) { const config = server.config(); const ccs = req.payload.ccs; const clusterUuid = req.params.clusterUuid; diff --git a/x-pack/plugins/monitoring/server/types.ts b/x-pack/plugins/monitoring/server/types.ts index 84b331df8ba42..4fbc1c494f14c 100644 --- a/x-pack/plugins/monitoring/server/types.ts +++ b/x-pack/plugins/monitoring/server/types.ts @@ -17,6 +17,7 @@ import { LicensingPluginSetup } from '../../licensing/server'; import { PluginSetupContract as FeaturesPluginSetupContract } from '../../features/server'; import { EncryptedSavedObjectsPluginSetup } from '../../encrypted_saved_objects/server'; import { CloudSetup } from '../../cloud/server'; +import { ElasticsearchSource } from '../common/types/es'; export interface MonitoringLicenseService { refresh: () => Promise; @@ -81,30 +82,36 @@ export interface LegacyRequest { payload: { [key: string]: any; }; + params: { + [key: string]: string; + }; getKibanaStatsCollector: () => any; getUiSettingsService: () => any; getActionTypeRegistry: () => any; getAlertsClient: () => any; getActionsClient: () => any; - server: { - config: () => { - get: (key: string) => string | undefined; + server: LegacyServer; +} + +export interface LegacyServer { + route: (params: any) => void; + config: () => { + get: (key: string) => string | undefined; + }; + newPlatform: { + setup: { + plugins: PluginsSetup; }; - newPlatform: { - setup: { - plugins: PluginsSetup; - }; + }; + plugins: { + monitoring: { + info: MonitoringLicenseService; }; - plugins: { - monitoring: { - info: MonitoringLicenseService; - }; - elasticsearch: { - getCluster: ( - name: string - ) => { - callWithRequest: (req: any, endpoint: string, params: any) => Promise; - }; + elasticsearch: { + getCluster: ( + name: string + ) => { + callWithRequest: (req: any, endpoint: string, params: any) => Promise; }; }; }; @@ -132,57 +139,3 @@ export interface ElasticsearchResponseHit { }; }; } - -export interface ElasticsearchSource { - timestamp: string; - beats_stats?: { - timestamp?: string; - beat?: { - uuid?: string; - name?: string; - type?: string; - version?: string; - host?: string; - }; - metrics?: { - beat?: { - memstats?: { - memory_alloc?: number; - }; - info?: { - uptime?: { - ms?: number; - }; - }; - handles?: { - limit?: { - hard?: number; - soft?: number; - }; - }; - }; - libbeat?: { - config?: { - reloads?: number; - }; - output?: { - type?: string; - write?: { - bytes?: number; - errors?: number; - }; - read?: { - errors?: number; - }; - }; - pipeline?: { - events?: { - total?: number; - published?: number; - dropped?: number; - }; - }; - }; - }; - }; -} diff --git a/x-pack/plugins/observability/public/components/app/section/ux/index.test.tsx b/x-pack/plugins/observability/public/components/app/section/ux/index.test.tsx index be6df55166387..7b36fa6d56210 100644 --- a/x-pack/plugins/observability/public/components/app/section/ux/index.test.tsx +++ b/x-pack/plugins/observability/public/components/app/section/ux/index.test.tsx @@ -67,7 +67,7 @@ describe('UXSection', () => { expect(getByText('Largest contentful paint')).toBeInTheDocument(); expect(getByText('1.94 s')).toBeInTheDocument(); expect(getByText('14 ms')).toBeInTheDocument(); - expect(getByText('0.01')).toBeInTheDocument(); + expect(getByText('0.010')).toBeInTheDocument(); // LCP Rank Values expect(getByText('Good (65%)')).toBeInTheDocument(); diff --git a/x-pack/plugins/observability/public/components/app/section/ux/mock_data/ux.mock.ts b/x-pack/plugins/observability/public/components/app/section/ux/mock_data/ux.mock.ts index 017f385d36735..bbe81699e999d 100644 --- a/x-pack/plugins/observability/public/components/app/section/ux/mock_data/ux.mock.ts +++ b/x-pack/plugins/observability/public/components/app/section/ux/mock_data/ux.mock.ts @@ -9,7 +9,7 @@ import { UxFetchDataResponse } from '../../../../../typings'; export const response: UxFetchDataResponse = { appLink: '/app/ux', coreWebVitals: { - cls: '0.01', + cls: 0.01, fid: 13.5, lcp: 1942.6666666666667, tbt: 281.55833333333334, diff --git a/x-pack/plugins/observability/public/components/shared/core_web_vitals/__stories__/core_vitals.stories.tsx b/x-pack/plugins/observability/public/components/shared/core_web_vitals/__stories__/core_vitals.stories.tsx index 26cf9c144b4a1..208c840b403e9 100644 --- a/x-pack/plugins/observability/public/components/shared/core_web_vitals/__stories__/core_vitals.stories.tsx +++ b/x-pack/plugins/observability/public/components/shared/core_web_vitals/__stories__/core_vitals.stories.tsx @@ -33,13 +33,26 @@ export default { ], }; -export function Basic() { +export function NoDataAvailable() { + return ( + + ); +} + +export function OneHundredPercentGood() { return ( ); diff --git a/x-pack/plugins/observability/public/components/shared/core_web_vitals/core_vital_item.test.tsx b/x-pack/plugins/observability/public/components/shared/core_web_vitals/core_vital_item.test.tsx new file mode 100644 index 0000000000000..346355e11c6ef --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/core_web_vitals/core_vital_item.test.tsx @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import { render } from '../../../utils/test_helper'; +import { CoreVitalItem } from './core_vital_item'; +import { NO_DATA } from './translations'; + +describe('CoreVitalItem', () => { + const value = '0.005'; + const title = 'Cumulative Layout Shift'; + const thresholds = { bad: '0.25', good: '0.1' }; + const loading = false; + const helpLabel = 'sample help label'; + + it('renders if value is truthy', () => { + const { getByText } = render( + + ); + + expect(getByText(title)).toBeInTheDocument(); + expect(getByText(value)).toBeInTheDocument(); + expect(getByText('Good (85%)')).toBeInTheDocument(); + expect(getByText('Needs improvement (10%)')).toBeInTheDocument(); + expect(getByText('Poor (5%)')).toBeInTheDocument(); + }); + + it('renders loading state when loading is truthy', () => { + const { queryByText, getByText } = render( + + ); + + expect(queryByText(value)).not.toBeInTheDocument(); + expect(getByText('--')).toBeInTheDocument(); + }); + + it('renders no data UI if value is falsey and loading is falsey', () => { + const { getByText } = render( + + ); + + expect(getByText(NO_DATA)).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/observability/public/components/shared/core_web_vitals/core_vital_item.tsx b/x-pack/plugins/observability/public/components/shared/core_web_vitals/core_vital_item.tsx index 18831565b8784..23dd0b86a235b 100644 --- a/x-pack/plugins/observability/public/components/shared/core_web_vitals/core_vital_item.tsx +++ b/x-pack/plugins/observability/public/components/shared/core_web_vitals/core_vital_item.tsx @@ -88,12 +88,14 @@ export function CoreVitalItem({ const biggestValIndex = ranks.indexOf(Math.max(...ranks)); - if ((value === null || value !== undefined) && ranks[0] === 100 && !loading) { + if (!value && !loading) { return ; } + return ( <> { title: 'User Experience', appLink: '/ux', coreWebVitals: { - cls: '0.01', + cls: 0.01, fid: 5, lcp: 1464.3333333333333, tbt: 232.92166666666665, @@ -298,7 +298,7 @@ describe('registerDataHandler', () => { title: 'User Experience', appLink: '/ux', coreWebVitals: { - cls: '0.01', + cls: 0.01, fid: 5, lcp: 1464.3333333333333, tbt: 232.92166666666665, diff --git a/x-pack/plugins/reporting/common/index.ts b/x-pack/plugins/reporting/common/index.ts index 0be6ab6682774..467cc9f4d04cf 100644 --- a/x-pack/plugins/reporting/common/index.ts +++ b/x-pack/plugins/reporting/common/index.ts @@ -6,6 +6,7 @@ import { LayoutSelectorDictionary } from './types'; +export * as constants from './constants'; export { CancellationToken } from './cancellation_token'; export { Poller } from './poller'; diff --git a/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx b/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx index 7f48b5d9101ba..bbdc2e1aebe77 100644 --- a/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx +++ b/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx @@ -20,11 +20,10 @@ export interface Props { reportType: string; layoutId: string | undefined; objectId?: string; - objectType: string; getJobParams: () => BaseParams; options?: ReactElement; - isDirty: boolean; - onClose: () => void; + isDirty?: boolean; + onClose?: () => void; intl: InjectedIntl; } @@ -32,6 +31,7 @@ interface State { isStale: boolean; absoluteUrl: string; layoutId: string; + objectType: string; } class ReportingPanelContentUi extends Component { @@ -40,10 +40,14 @@ class ReportingPanelContentUi extends Component { constructor(props: Props) { super(props); + // Get objectType from job params + const { objectType } = props.getJobParams(); + this.state = { isStale: false, absoluteUrl: this.getAbsoluteReportGenerationUrl(props), layoutId: '', + objectType, }; } @@ -104,7 +108,7 @@ class ReportingPanelContentUi extends Component { description="Here 'reportingType' can be 'PDF' or 'CSV'" values={{ reportingType: this.prettyPrintReportingType(), - objectType: this.props.objectType, + objectType: this.state.objectType, }} /> ); @@ -209,7 +213,7 @@ class ReportingPanelContentUi extends Component { id: 'xpack.reporting.panelContent.successfullyQueuedReportNotificationTitle', defaultMessage: 'Queued report for {objectType}', }, - { objectType: this.props.objectType } + { objectType: this.state.objectType } ), text: toMountPoint( { ), 'data-test-subj': 'queueReportSuccess', }); - this.props.onClose(); + if (this.props.onClose) { + this.props.onClose(); + } }) .catch((error: any) => { if (error.message === 'not exportable') { @@ -229,7 +235,7 @@ class ReportingPanelContentUi extends Component { id: 'xpack.reporting.panelContent.whatCanBeExportedWarningTitle', defaultMessage: 'Only saved {objectType} can be exported', }, - { objectType: this.props.objectType } + { objectType: this.state.objectType } ), text: toMountPoint( BaseParams; - isDirty: boolean; - onClose: () => void; + isDirty?: boolean; + onClose?: () => void; } interface State { @@ -32,8 +31,8 @@ export class ScreenCapturePanelContent extends Component { constructor(props: Props) { super(props); - const isPreserveLayoutSupported = - props.reportType !== 'png' && props.objectType !== 'visualization'; + const { objectType } = props.getJobParams(); + const isPreserveLayoutSupported = props.reportType !== 'png' && objectType !== 'visualization'; this.state = { isPreserveLayoutSupported, usePrintLayout: false, @@ -47,7 +46,6 @@ export class ScreenCapturePanelContent extends Component { toasts={this.props.toasts} reportType={this.props.reportType} layoutId={this.getLayout().id} - objectType={this.props.objectType} objectId={this.props.objectId} getJobParams={this.getJobParams} options={this.renderOptions()} diff --git a/x-pack/plugins/reporting/public/index.ts b/x-pack/plugins/reporting/public/index.ts index f15a5ca481757..39013ba171373 100644 --- a/x-pack/plugins/reporting/public/index.ts +++ b/x-pack/plugins/reporting/public/index.ts @@ -5,6 +5,7 @@ */ import { PluginInitializerContext } from 'src/core/public'; +import { getDefaultLayoutSelectors } from '../common'; import { ScreenCapturePanelContent } from './components/screen_capture_panel_content'; import * as jobCompletionNotifications from './lib/job_completion_notifications'; import { ReportingAPIClient } from './lib/reporting_api_client'; @@ -14,10 +15,13 @@ export interface ReportingSetup { components: { ScreenCapturePanel: typeof ScreenCapturePanelContent; }; + getDefaultLayoutSelectors: typeof getDefaultLayoutSelectors; + ReportingAPIClient: typeof ReportingAPIClient; } export type ReportingStart = ReportingSetup; +export { constants, getDefaultLayoutSelectors } from '../common'; export { ReportingAPIClient, ReportingPublicPlugin as Plugin, jobCompletionNotifications }; export function plugin(initializerContext: PluginInitializerContext) { diff --git a/x-pack/plugins/reporting/public/plugin.ts b/x-pack/plugins/reporting/public/plugin.ts index 52362b4c68734..3a5a6a50616aa 100644 --- a/x-pack/plugins/reporting/public/plugin.ts +++ b/x-pack/plugins/reporting/public/plugin.ts @@ -24,7 +24,7 @@ import { import { ManagementSetup, ManagementStart } from '../../../../src/plugins/management/public'; import { SharePluginSetup, SharePluginStart } from '../../../../src/plugins/share/public'; import { LicensingPluginSetup, LicensingPluginStart } from '../../licensing/public'; -import { JOB_COMPLETION_NOTIFICATIONS_SESSION_KEY } from '../common/constants'; +import { constants, getDefaultLayoutSelectors } from '../common'; import { durationToNumber } from '../common/schema_utils'; import { JobId, JobSummarySet } from '../common/types'; import { ReportingSetup, ReportingStart } from './'; @@ -48,7 +48,7 @@ export interface ClientConfigType { } function getStored(): JobId[] { - const sessionValue = sessionStorage.getItem(JOB_COMPLETION_NOTIFICATIONS_SESSION_KEY); + const sessionValue = sessionStorage.getItem(constants.JOB_COMPLETION_NOTIFICATIONS_SESSION_KEY); return sessionValue ? JSON.parse(sessionValue) : []; } @@ -89,7 +89,11 @@ export class ReportingPublicPlugin ReportingPublicPluginSetupDendencies, ReportingPublicPluginStartDendencies > { - private readonly contract: ReportingStart = { components: { ScreenCapturePanel } }; + private readonly contract: ReportingStart = { + components: { ScreenCapturePanel }, + getDefaultLayoutSelectors, + ReportingAPIClient, + }; private readonly stop$ = new Rx.ReplaySubject(1); private readonly title = i18n.translate('xpack.reporting.management.reportingTitle', { defaultMessage: 'Reporting', diff --git a/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx b/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx index e90d6786b58f2..7126762c0f4ee 100644 --- a/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx +++ b/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx @@ -97,7 +97,6 @@ export const csvReportingProvider = ({ toasts={toasts} reportType="csv" layoutId={undefined} - objectType={objectType} objectId={objectId} getJobParams={getJobParams} isDirty={isDirty} diff --git a/x-pack/plugins/reporting/public/share_context_menu/register_pdf_png_reporting.tsx b/x-pack/plugins/reporting/public/share_context_menu/register_pdf_png_reporting.tsx index d17d4af3c0102..f0f379ae032ae 100644 --- a/x-pack/plugins/reporting/public/share_context_menu/register_pdf_png_reporting.tsx +++ b/x-pack/plugins/reporting/public/share_context_menu/register_pdf_png_reporting.tsx @@ -135,7 +135,6 @@ export const reportingPDFPNGProvider = ({ apiClient={apiClient} toasts={toasts} reportType="png" - objectType={objectType} objectId={objectId} getJobParams={getPngJobParams} isDirty={isDirty} @@ -162,7 +161,6 @@ export const reportingPDFPNGProvider = ({ apiClient={apiClient} toasts={toasts} reportType="printablePdf" - objectType={objectType} objectId={objectId} getJobParams={getPdfJobParams} isDirty={isDirty} diff --git a/x-pack/plugins/reporting/server/usage/get_reporting_usage.ts b/x-pack/plugins/reporting/server/usage/get_reporting_usage.ts index 1211d4c2cf1c3..6d7f5c2e367fa 100644 --- a/x-pack/plugins/reporting/server/usage/get_reporting_usage.ts +++ b/x-pack/plugins/reporting/server/usage/get_reporting_usage.ts @@ -5,7 +5,7 @@ */ import { get } from 'lodash'; -import { LegacyAPICaller } from 'kibana/server'; +import { ElasticsearchClient, SearchResponse } from 'kibana/server'; import { ReportingConfig } from '../'; import { ExportTypesRegistry } from '../lib/export_types_registry'; import { GetLicense } from './'; @@ -18,7 +18,7 @@ import { KeyCountBucket, RangeStats, ReportingUsageType, - SearchResponse, + ReportingUsageSearchResponse, StatusByAppBucket, } from './types'; @@ -99,7 +99,9 @@ type RangeStatSets = Partial & { last7Days: Partial; }; -async function handleResponse(response: SearchResponse): Promise> { +type ESResponse = Partial>; + +async function handleResponse(response: ESResponse): Promise> { const buckets = get(response, 'aggregations.ranges.buckets'); if (!buckets) { return {}; @@ -118,7 +120,7 @@ async function handleResponse(response: SearchResponse): Promise { const reportingIndex = config.get('index'); @@ -165,8 +167,9 @@ export async function getReportingUsage( }; const featureAvailability = await getLicense(); - return callCluster('search', params) - .then((response: SearchResponse) => handleResponse(response)) + return esClient + .search(params) + .then(({ body: response }) => handleResponse(response)) .then( (usage: Partial): ReportingUsageType => { // Allow this to explicitly throw an exception if/when this config is deprecated, diff --git a/x-pack/plugins/reporting/server/usage/reporting_usage_collector.test.ts b/x-pack/plugins/reporting/server/usage/reporting_usage_collector.test.ts index 7cae5e9b6f956..8b0c442c12b97 100644 --- a/x-pack/plugins/reporting/server/usage/reporting_usage_collector.test.ts +++ b/x-pack/plugins/reporting/server/usage/reporting_usage_collector.test.ts @@ -59,7 +59,7 @@ const getResponseMock = (base = {}) => base; const getMockFetchClients = (resp: any) => { const fetchParamsMock = createCollectorFetchContextMock(); - fetchParamsMock.callCluster.mockResolvedValue(resp); + fetchParamsMock.esClient.search = jest.fn().mockResolvedValue({ body: resp }); return fetchParamsMock; }; describe('license checks', () => { diff --git a/x-pack/plugins/reporting/server/usage/reporting_usage_collector.ts b/x-pack/plugins/reporting/server/usage/reporting_usage_collector.ts index f4209730b68ce..547c331784c5f 100644 --- a/x-pack/plugins/reporting/server/usage/reporting_usage_collector.ts +++ b/x-pack/plugins/reporting/server/usage/reporting_usage_collector.ts @@ -26,9 +26,9 @@ export function getReportingUsageCollector( ) { return usageCollection.makeUsageCollector({ type: 'reporting', - fetch: ({ callCluster }: CollectorFetchContext) => { + fetch: ({ esClient }: CollectorFetchContext) => { const config = reporting.getConfig(); - return getReportingUsage(config, getLicense, callCluster, exportTypesRegistry); + return getReportingUsage(config, getLicense, esClient, exportTypesRegistry); }, isReady, schema: reportingSchema, diff --git a/x-pack/plugins/reporting/server/usage/types.ts b/x-pack/plugins/reporting/server/usage/types.ts index 1ff680eff8eaf..fe7838240f2fa 100644 --- a/x-pack/plugins/reporting/server/usage/types.ts +++ b/x-pack/plugins/reporting/server/usage/types.ts @@ -152,7 +152,7 @@ export interface AggregationResultBuckets { doc_count: number; } -export interface SearchResponse { +export interface ReportingUsageSearchResponse { aggregations: { ranges: { buckets: { diff --git a/x-pack/plugins/rollup/server/collectors/register.ts b/x-pack/plugins/rollup/server/collectors/register.ts index 33bb430aefe5e..ea7be9e76c9f2 100644 --- a/x-pack/plugins/rollup/server/collectors/register.ts +++ b/x-pack/plugins/rollup/server/collectors/register.ts @@ -6,7 +6,7 @@ import { get } from 'lodash'; import { UsageCollectionSetup, CollectorFetchContext } from 'src/plugins/usage_collection/server'; -import { LegacyAPICaller } from 'kibana/server'; +import { ElasticsearchClient } from 'kibana/server'; interface IdToFlagMap { [key: string]: boolean; @@ -27,7 +27,7 @@ function createIdToFlagMap(ids: string[]) { }, {} as any); } -async function fetchRollupIndexPatterns(kibanaIndex: string, callCluster: LegacyAPICaller) { +async function fetchRollupIndexPatterns(kibanaIndex: string, esClient: ElasticsearchClient) { const searchParams = { size: ES_MAX_RESULT_WINDOW_DEFAULT_VALUE, index: kibanaIndex, @@ -46,7 +46,7 @@ async function fetchRollupIndexPatterns(kibanaIndex: string, callCluster: Legacy }, }; - const esResponse = await callCluster('search', searchParams); + const { body: esResponse } = await esClient.search(searchParams); return get(esResponse, 'hits.hits', []).map((indexPattern: any) => { const { _id: savedObjectId } = indexPattern; @@ -56,7 +56,7 @@ async function fetchRollupIndexPatterns(kibanaIndex: string, callCluster: Legacy async function fetchRollupSavedSearches( kibanaIndex: string, - callCluster: LegacyAPICaller, + esClient: ElasticsearchClient, rollupIndexPatternToFlagMap: IdToFlagMap ) { const searchParams = { @@ -77,7 +77,7 @@ async function fetchRollupSavedSearches( }, }; - const esResponse = await callCluster('search', searchParams); + const { body: esResponse } = await esClient.search(searchParams); const savedSearches = get(esResponse, 'hits.hits', []); // Filter for ones with rollup index patterns. @@ -104,7 +104,7 @@ async function fetchRollupSavedSearches( async function fetchRollupVisualizations( kibanaIndex: string, - callCluster: LegacyAPICaller, + esClient: ElasticsearchClient, rollupIndexPatternToFlagMap: IdToFlagMap, rollupSavedSearchesToFlagMap: IdToFlagMap ) { @@ -130,7 +130,7 @@ async function fetchRollupVisualizations( }, }; - const esResponse = await callCluster('search', searchParams); + const { body: esResponse } = await esClient.search(searchParams); const visualizations = get(esResponse, 'hits.hits', []); let rollupVisualizations = 0; @@ -211,13 +211,13 @@ export function registerRollupUsageCollector( total: { type: 'long' }, }, }, - fetch: async ({ callCluster }: CollectorFetchContext) => { - const rollupIndexPatterns = await fetchRollupIndexPatterns(kibanaIndex, callCluster); + fetch: async ({ esClient }: CollectorFetchContext) => { + const rollupIndexPatterns = await fetchRollupIndexPatterns(kibanaIndex, esClient); const rollupIndexPatternToFlagMap = createIdToFlagMap(rollupIndexPatterns); const rollupSavedSearches = await fetchRollupSavedSearches( kibanaIndex, - callCluster, + esClient, rollupIndexPatternToFlagMap ); const rollupSavedSearchesToFlagMap = createIdToFlagMap(rollupSavedSearches); @@ -227,7 +227,7 @@ export function registerRollupUsageCollector( rollupVisualizationsFromSavedSearches, } = await fetchRollupVisualizations( kibanaIndex, - callCluster, + esClient, rollupIndexPatternToFlagMap, rollupSavedSearchesToFlagMap ); diff --git a/x-pack/plugins/security/server/authentication/api_keys/api_keys.test.ts b/x-pack/plugins/security/server/authentication/api_keys/api_keys.test.ts index 1e5d3baab83e6..c43ed9e1248f6 100644 --- a/x-pack/plugins/security/server/authentication/api_keys/api_keys.test.ts +++ b/x-pack/plugins/security/server/authentication/api_keys/api_keys.test.ts @@ -115,7 +115,7 @@ describe('API Keys', () => { expect(result).toEqual(true); expect(mockClusterClient.asInternalUser.security.invalidateApiKey).toHaveBeenCalledWith({ body: { - id: 'kibana-api-key-service-test', + ids: ['kibana-api-key-service-test'], }, }); }); @@ -318,7 +318,7 @@ describe('API Keys', () => { }); expect(mockScopedClusterClient.asCurrentUser.security.invalidateApiKey).toHaveBeenCalledWith({ body: { - id: '123', + ids: ['123'], }, }); }); @@ -345,7 +345,7 @@ describe('API Keys', () => { }); expect(mockScopedClusterClient.asCurrentUser.security.invalidateApiKey).toHaveBeenCalledWith({ body: { - id: '123', + ids: ['123'], }, }); }); @@ -378,7 +378,7 @@ describe('API Keys', () => { }); expect(mockClusterClient.asInternalUser.security.invalidateApiKey).toHaveBeenCalledWith({ body: { - id: '123', + ids: ['123'], }, }); }); @@ -405,7 +405,7 @@ describe('API Keys', () => { }); expect(mockClusterClient.asInternalUser.security.invalidateApiKey).toHaveBeenCalledWith({ body: { - id: '123', + ids: ['123'], }, }); }); diff --git a/x-pack/plugins/security/server/authentication/api_keys/api_keys.ts b/x-pack/plugins/security/server/authentication/api_keys/api_keys.ts index 212b5755549f9..a42efb678fcea 100644 --- a/x-pack/plugins/security/server/authentication/api_keys/api_keys.ts +++ b/x-pack/plugins/security/server/authentication/api_keys/api_keys.ts @@ -143,7 +143,7 @@ export class APIKeys { ); try { - await this.clusterClient.asInternalUser.security.invalidateApiKey({ body: { id } }); + await this.clusterClient.asInternalUser.security.invalidateApiKey({ body: { ids: [id] } }); return true; } catch (e) { if (this.doesErrorIndicateAPIKeysAreDisabled(e)) { @@ -240,7 +240,7 @@ export class APIKeys { await this.clusterClient .asScoped(request) .asCurrentUser.security.invalidateApiKey({ - body: { id: params.id }, + body: { ids: [params.id] }, }) ).body; this.logger.debug('API key was invalidated successfully as current user'); @@ -268,7 +268,7 @@ export class APIKeys { // Internal user needs `cluster:admin/xpack/security/api_key/invalidate` privilege to use this API result = ( await this.clusterClient.asInternalUser.security.invalidateApiKey({ - body: { id: params.id }, + body: { ids: [params.id] }, }) ).body; this.logger.debug('API key was invalidated successfully'); diff --git a/x-pack/plugins/security/server/authorization/__snapshots__/reset_session_page.test.tsx.snap b/x-pack/plugins/security/server/authorization/__snapshots__/reset_session_page.test.tsx.snap index 194f6301b11ea..75886b4573edd 100644 --- a/x-pack/plugins/security/server/authorization/__snapshots__/reset_session_page.test.tsx.snap +++ b/x-pack/plugins/security/server/authorization/__snapshots__/reset_session_page.test.tsx.snap @@ -1,3 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`ResetSessionPage renders as expected 1`] = `"MockedFonts

    You do not have permission to access the requested page

    Either go back to the previous page or log in as a different user.

    "`; +exports[`ResetSessionPage renders as expected 1`] = `"MockedFonts

    You do not have permission to access the requested page

    Either go back to the previous page or log in as a different user.

    "`; diff --git a/x-pack/plugins/security/server/routes/api_keys/invalidate.test.ts b/x-pack/plugins/security/server/routes/api_keys/invalidate.test.ts index 9ac41fdfa7483..88a84462e8a66 100644 --- a/x-pack/plugins/security/server/routes/api_keys/invalidate.test.ts +++ b/x-pack/plugins/security/server/routes/api_keys/invalidate.test.ts @@ -127,7 +127,7 @@ describe('Invalidate API keys', () => { isAdmin: true, }, asserts: { - apiArguments: [{ body: { id: 'si8If24B1bKsmSLTAhJV' } }], + apiArguments: [{ body: { ids: ['si8If24B1bKsmSLTAhJV'] } }], statusCode: 200, result: { itemsInvalidated: [], @@ -151,7 +151,7 @@ describe('Invalidate API keys', () => { isAdmin: true, }, asserts: { - apiArguments: [{ body: { id: 'si8If24B1bKsmSLTAhJV' } }], + apiArguments: [{ body: { ids: ['si8If24B1bKsmSLTAhJV'] } }], statusCode: 200, result: { itemsInvalidated: [{ id: 'si8If24B1bKsmSLTAhJV', name: 'my-api-key' }], @@ -167,7 +167,7 @@ describe('Invalidate API keys', () => { isAdmin: false, }, asserts: { - apiArguments: [{ body: { id: 'si8If24B1bKsmSLTAhJV', owner: true } }], + apiArguments: [{ body: { ids: ['si8If24B1bKsmSLTAhJV'], owner: true } }], statusCode: 200, result: { itemsInvalidated: [{ id: 'si8If24B1bKsmSLTAhJV', name: 'my-api-key' }], @@ -192,8 +192,8 @@ describe('Invalidate API keys', () => { }, asserts: { apiArguments: [ - { body: { id: 'si8If24B1bKsmSLTAhJV' } }, - { body: { id: 'ab8If24B1bKsmSLTAhNC' } }, + { body: { ids: ['si8If24B1bKsmSLTAhJV'] } }, + { body: { ids: ['ab8If24B1bKsmSLTAhNC'] } }, ], statusCode: 200, result: { diff --git a/x-pack/plugins/security/server/routes/api_keys/invalidate.ts b/x-pack/plugins/security/server/routes/api_keys/invalidate.ts index 3977954197007..9e89b4593f023 100644 --- a/x-pack/plugins/security/server/routes/api_keys/invalidate.ts +++ b/x-pack/plugins/security/server/routes/api_keys/invalidate.ts @@ -33,7 +33,7 @@ export function defineInvalidateApiKeysRoutes({ router }: RouteDefinitionParams) await Promise.all( request.body.apiKeys.map(async (key) => { try { - const body: { id: string; owner?: boolean } = { id: key.id }; + const body: { ids: string[]; owner?: boolean } = { ids: [key.id] }; if (!request.body.isAdmin) { body.owner = true; } diff --git a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts index 34c5ab0ded17b..fafd0c2772842 100644 --- a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts @@ -1332,6 +1332,7 @@ export class EndpointDocGenerator { { id: 'logs-endpoint.events.security', type: 'index_template' }, { id: 'metrics-endpoint.telemetry', type: 'index_template' }, ] as EsAssetReference[], + package_assets: [], es_index_patterns: { alerts: 'logs-endpoint.alerts-*', events: 'events-endpoint-*', diff --git a/x-pack/plugins/security_solution/common/endpoint/index_data.ts b/x-pack/plugins/security_solution/common/endpoint/index_data.ts index 56bc315d6e9e4..b3259b19cf2c0 100644 --- a/x-pack/plugins/security_solution/common/endpoint/index_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/index_data.ts @@ -232,8 +232,8 @@ const createPolicy = async ( ): Promise => { // Create Agent Policy first const newAgentPolicyData: CreateAgentPolicyRequest['body'] = { - name: `Policy for ${policyName}`, - description: '', + name: `Policy for ${policyName} (${Math.random().toString(36).substr(2, 5)})`, + description: `Policy created with endpoint data generator (${policyName})`, namespace: 'default', }; let agentPolicy; @@ -368,12 +368,7 @@ const fleetEnrollAgentForHost = async ( }, }, host: { - architecture: 'x86_64', - hostname: endpointHost.host, - name: endpointHost.host, - id: '1c032ec0-3a94-4d54-9ad2-c5610c0eaba4', - ip: ['fe80::703b:b9e6:887d:7f5/64', '10.0.2.15/24', '::1/128', '127.0.0.1/8'], - mac: ['08:00:27:d8:c5:c0'], + ...endpointHost.host, }, os: { family: 'windows', diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/resolver.ts b/x-pack/plugins/security_solution/common/endpoint/schema/resolver.ts index af3628b720749..fd5c2c35a1549 100644 --- a/x-pack/plugins/security_solution/common/endpoint/schema/resolver.ts +++ b/x-pack/plugins/security_solution/common/endpoint/schema/resolver.ts @@ -6,23 +6,6 @@ import { schema } from '@kbn/config-schema'; -/** - * Used to validate GET requests for a complete resolver tree centered around an entity_id. - */ -export const validateTreeEntityID = { - params: schema.object({ id: schema.string({ minLength: 1 }) }), - query: schema.object({ - children: schema.number({ defaultValue: 200, min: 0, max: 10000 }), - ancestors: schema.number({ defaultValue: 200, min: 0, max: 10000 }), - events: schema.number({ defaultValue: 1000, min: 0, max: 10000 }), - alerts: schema.number({ defaultValue: 1000, min: 0, max: 10000 }), - afterEvent: schema.maybe(schema.string()), - afterAlert: schema.maybe(schema.string()), - afterChild: schema.maybe(schema.string()), - legacyEndpointID: schema.maybe(schema.string({ minLength: 1 })), - }), -}; - /** * Used to validate GET requests for a complete resolver tree. */ @@ -80,46 +63,6 @@ export const validateEvents = { }), }; -/** - * Used to validate GET requests for alerts for a specific process. - */ -export const validateAlerts = { - params: schema.object({ id: schema.string({ minLength: 1 }) }), - query: schema.object({ - alerts: schema.number({ defaultValue: 1000, min: 1, max: 10000 }), - afterAlert: schema.maybe(schema.string()), - legacyEndpointID: schema.maybe(schema.string({ minLength: 1 })), - }), - body: schema.nullable( - schema.object({ - filter: schema.maybe(schema.string()), - }) - ), -}; - -/** - * Used to validate GET requests for the ancestors of a process event. - */ -export const validateAncestry = { - params: schema.object({ id: schema.string({ minLength: 1 }) }), - query: schema.object({ - ancestors: schema.number({ defaultValue: 200, min: 0, max: 10000 }), - legacyEndpointID: schema.maybe(schema.string({ minLength: 1 })), - }), -}; - -/** - * Used to validate GET requests for children of a specified process event. - */ -export const validateChildren = { - params: schema.object({ id: schema.string({ minLength: 1 }) }), - query: schema.object({ - children: schema.number({ defaultValue: 200, min: 1, max: 10000 }), - afterChild: schema.maybe(schema.string()), - legacyEndpointID: schema.maybe(schema.string({ minLength: 1 })), - }), -}; - /** * Used to validate GET requests for 'entities' */ diff --git a/x-pack/plugins/security_solution/common/endpoint/types/index.ts b/x-pack/plugins/security_solution/common/endpoint/types/index.ts index 94fa448840c42..fab5bd9daae00 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/index.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/index.ts @@ -139,207 +139,6 @@ export interface NewResolverTree { nodes: ResolverNode[]; } -/** - * Statistical information for a node in a resolver tree. - * @deprecated use {@link EventStats} instead to model the stats for a node - */ -export interface ResolverNodeStats { - /** - * The stats for related events (excludes alerts and process events) for a particular node in the resolver tree. - */ - events: EventStats; - /** - * The total number of alerts that exist for a node. - */ - totalAlerts: number; -} - -/** - * A child node can also have additional children so we need to provide a pagination cursor. - * - * @deprecated use {@link ResolverNode} instead - */ -export interface ResolverChildNode extends ResolverLifecycleNode { - /** - * nextChild can have 3 different states: - * - * undefined: This indicates that you should not use this node for additional queries. It does not mean that node does - * not have any more direct children. The node could have more direct children but to determine that, use the - * ResolverChildren node's nextChild. - * - * null: Indicates that we have received all the children of the node. There may be more descendants though. - * - * string: Indicates this is a leaf node and it can be used to continue querying for additional descendants - * using this node's entity_id - * - * For more information see the resolver docs on pagination [here](../../server/endpoint/routes/resolver/docs/README.md#L129) - */ - nextChild?: string | null; -} - -/** - * Safe version of `ResolverChildNode`. - * - * @deprecated use {@link ResolverNode} instead - */ -export interface SafeResolverChildNode extends SafeResolverLifecycleNode { - /** - * nextChild can have 3 different states: - * - * undefined: This indicates that you should not use this node for additional queries. It does not mean that node does - * not have any more direct children. The node could have more direct children but to determine that, use the - * ResolverChildren node's nextChild. - * - * null: Indicates that we have received all the children of the node. There may be more descendants though. - * - * string: Indicates this is a leaf node and it can be used to continue querying for additional descendants - * using this node's entity_id - * - * For more information see the resolver docs on pagination [here](../../server/endpoint/routes/resolver/docs/README.md#L129) - */ - nextChild?: string | null; -} - -/** - * The response structure for the children route. The structure is an array of nodes where each node - * has an array of lifecycle events. - * - * @deprecated use {@link ResolverNode} instead - */ -export interface ResolverChildren { - childNodes: ResolverChildNode[]; - /** - * nextChild can have 2 different states: - * - * null: Indicates that we have received all the descendants that can be retrieved using this node. To retrieve more - * nodes in the tree use a cursor provided in one of the returned children. If no other cursor exists then the tree - * is complete. - * - * string: Indicates this node has more descendants that can be retrieved, pass this cursor in while using this node's - * entity_id for the request. - */ - nextChild: string | null; -} - -/** - * Safe version of `ResolverChildren`. - * - * @deprecated use {@link ResolverNode} instead - */ -export interface SafeResolverChildren { - childNodes: SafeResolverChildNode[]; - /** - * nextChild can have 2 different states: - * - * null: Indicates that we have received all the descendants that can be retrieved using this node. To retrieve more - * nodes in the tree use a cursor provided in one of the returned children. If no other cursor exists then the tree - * is complete. - * - * string: Indicates this node has more descendants that can be retrieved, pass this cursor in while using this node's - * entity_id for the request. - */ - nextChild: string | null; -} - -/** - * A flattened tree representing the nodes in a resolver graph. - * - * @deprecated use {@link ResolverNode} instead - */ -export interface ResolverTree { - /** - * Origin of the tree. This is in the middle of the tree. Typically this would be the same - * process node that generated an alert. - */ - entityID: string; - children: ResolverChildren; - relatedEvents: Omit; - relatedAlerts: Omit; - ancestry: ResolverAncestry; - lifecycle: SafeResolverEvent[]; - stats: ResolverNodeStats; -} - -/** - * Safe version of `ResolverTree`. - * - * @deprecated use {@link ResolverNode} instead - */ -export interface SafeResolverTree { - /** - * Origin of the tree. This is in the middle of the tree. Typically this would be the same - * process node that generated an alert. - */ - entityID: string; - children: SafeResolverChildren; - relatedAlerts: Omit; - ancestry: SafeResolverAncestry; - lifecycle: SafeResolverEvent[]; - stats: ResolverNodeStats; -} - -/** - * The lifecycle events (start, end etc) for a node. - * - * @deprecated use {@link ResolverNode} instead - */ -export interface ResolverLifecycleNode { - entityID: string; - lifecycle: SafeResolverEvent[]; - /** - * stats are only set when the entire tree is being fetched - */ - stats?: ResolverNodeStats; -} - -/** - * Safe version of `ResolverLifecycleNode`. - * - * @deprecated use {@link ResolverNode} instead - */ -export interface SafeResolverLifecycleNode { - entityID: string; - lifecycle: SafeResolverEvent[]; - /** - * stats are only set when the entire tree is being fetched - */ - stats?: ResolverNodeStats; -} - -/** - * The response structure when searching for ancestors of a node. - * - * @deprecated use {@link ResolverNode} instead - */ -export interface ResolverAncestry { - /** - * An array of ancestors with the lifecycle events grouped together - */ - ancestors: ResolverLifecycleNode[]; - /** - * A cursor for retrieving additional ancestors for a particular node. `null` indicates that there were no additional - * ancestors when the request returned. More could have been ingested by ES after the fact though. - */ - nextAncestor: string | null; -} - -/** - * Safe version of `ResolverAncestry`. - * - * @deprecated use {@link ResolverNode} instead - */ -export interface SafeResolverAncestry { - /** - * An array of ancestors with the lifecycle events grouped together - */ - ancestors: SafeResolverLifecycleNode[]; - /** - * A cursor for retrieving additional ancestors for a particular node. `null` indicates that there were no additional - * ancestors when the request returned. More could have been ingested by ES after the fact though. - */ - nextAncestor: string | null; -} - /** * Response structure for the related events route. * @@ -360,15 +159,6 @@ export interface ResolverPaginatedEvents { nextEvent: string | null; } -/** - * Response structure for the alerts route. - */ -export interface ResolverRelatedAlerts { - entityID: string; - alerts: SafeResolverEvent[]; - nextAlert: string | null; -} - /** * Returned by the server via /api/endpoint/metadata */ diff --git a/x-pack/plugins/security_solution/common/types/timeline/index.ts b/x-pack/plugins/security_solution/common/types/timeline/index.ts index aa114ff074898..26d13b13f40cb 100644 --- a/x-pack/plugins/security_solution/common/types/timeline/index.ts +++ b/x-pack/plugins/security_solution/common/types/timeline/index.ts @@ -408,12 +408,23 @@ export type ImportTimelineResultSchema = runtimeTypes.TypeOf; -export type TimelineExpandedEvent = TimelineExpandedEventType | EmptyObject; +export type TimelineExpandedEventType = + | { + eventId: string; + indexName: string; + } + | EmptyObject; + +export type TimelineExpandedEvent = { + [tab in TimelineTabs]?: TimelineExpandedEventType; +}; diff --git a/x-pack/plugins/security_solution/cypress/README.md b/x-pack/plugins/security_solution/cypress/README.md index b82f4a392483c..4fb98f0983ee9 100644 --- a/x-pack/plugins/security_solution/cypress/README.md +++ b/x-pack/plugins/security_solution/cypress/README.md @@ -179,9 +179,9 @@ CYPRESS_BASE_URL=http(s)://:@ CYPRESS_ELASTICSEARCH_ ## Best Practices -### Clean up the state between tests +### Clean up the state -Remember to clean up the state of the test after its execution. +Remember to use the `cleanKibana` method before starting the execution of the test ### Minimize the use of es_archive @@ -192,15 +192,12 @@ When possible, create all the data that you need for executing the tests using t Loading the web page takes a big amount of time, in order to minimize that impact, the following points should be taken into consideration until another solution is implemented: -- Don't refresh the page for every test to clean the state of it. -- Instead, group the tests that are similar in different contexts. +- Group the tests that are similar in different contexts. - For every context login only once, clean the state between tests if needed without re-loading the page. - All tests in a spec file must be order-independent. - - If you need to reload the page to make the tests order-independent, consider to create a new context. - + Remember that minimizing the number of times the web page is loaded, we minimize as well the execution time. - ## Reporting When Cypress tests are run on the command line via non visual mode diff --git a/x-pack/plugins/security_solution/cypress/cypress.json b/x-pack/plugins/security_solution/cypress/cypress.json index 0eaa224101452..6feb9d794740d 100644 --- a/x-pack/plugins/security_solution/cypress/cypress.json +++ b/x-pack/plugins/security_solution/cypress/cypress.json @@ -1,6 +1,8 @@ { "baseUrl": "http://localhost:5601", "defaultCommandTimeout": 60000, + "execTimeout": 120000, + "nodeVersion": "system", "retries": { "runMode": 2 }, diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts.spec.ts index a15aad1bd8cc3..c409dbc7814fc 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts.spec.ts @@ -3,6 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import { newRule } from '../objects/rule'; import { ALERTS, ALERTS_COUNT, @@ -24,37 +25,35 @@ import { waitForAlertsToBeLoaded, markInProgressFirstAlert, goToInProgressAlerts, + waitForAlertsIndexToBeCreated, } from '../tasks/alerts'; -import { removeSignalsIndex } from '../tasks/api_calls/rules'; +import { createCustomRuleActivated } from '../tasks/api_calls/rules'; import { cleanKibana } from '../tasks/common'; -import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; +import { waitForAlertsToPopulate } from '../tasks/create_new_rule'; import { loginAndWaitForPage } from '../tasks/login'; +import { refreshPage } from '../tasks/security_header'; import { DETECTIONS_URL } from '../urls/navigation'; -describe('Alerts', () => { +describe.skip('Alerts', () => { context('Closing alerts', () => { beforeEach(() => { cleanKibana(); - removeSignalsIndex(); - esArchiverLoad('alerts'); loginAndWaitForPage(DETECTIONS_URL); - }); - - afterEach(() => { - esArchiverUnload('alerts'); + waitForAlertsPanelToBeLoaded(); + waitForAlertsIndexToBeCreated(); + createCustomRuleActivated(newRule); + refreshPage(); + waitForAlertsToPopulate(); }); it('Closes and opens alerts', () => { - waitForAlertsPanelToBeLoaded(); - waitForAlertsToBeLoaded(); - + const numberOfAlertsToBeClosed = 3; cy.get(ALERTS_COUNT) .invoke('text') .then((numberOfAlerts) => { cy.get(SHOWING_ALERTS).should('have.text', `Showing ${numberOfAlerts} alerts`); - const numberOfAlertsToBeClosed = 3; selectNumberOfAlerts(numberOfAlertsToBeClosed); cy.get(SELECTED_ALERTS).should( @@ -64,8 +63,6 @@ describe('Alerts', () => { closeAlerts(); waitForAlerts(); - cy.reload(); - waitForAlerts(); const expectedNumberOfAlertsAfterClosing = +numberOfAlerts - numberOfAlertsToBeClosed; cy.get(ALERTS_COUNT).should('have.text', expectedNumberOfAlertsAfterClosing.toString()); @@ -92,11 +89,6 @@ describe('Alerts', () => { openAlerts(); waitForAlerts(); - cy.reload(); - waitForAlertsToBeLoaded(); - waitForAlerts(); - goToClosedAlerts(); - waitForAlerts(); const expectedNumberOfClosedAlertsAfterOpened = 2; cy.get(ALERTS_COUNT).should( @@ -124,8 +116,6 @@ describe('Alerts', () => { }); it('Closes one alert when more than one opened alerts are selected', () => { - waitForAlertsToBeLoaded(); - cy.get(ALERTS_COUNT) .invoke('text') .then((numberOfAlerts) => { @@ -137,8 +127,6 @@ describe('Alerts', () => { cy.get(TAKE_ACTION_POPOVER_BTN).should('not.have.attr', 'disabled'); closeFirstAlert(); - cy.reload(); - waitForAlertsToBeLoaded(); waitForAlerts(); const expectedNumberOfAlerts = +numberOfAlerts - numberOfAlertsToBeClosed; @@ -164,52 +152,66 @@ describe('Alerts', () => { context('Opening alerts', () => { beforeEach(() => { cleanKibana(); - removeSignalsIndex(); - esArchiverLoad('closed_alerts'); loginAndWaitForPage(DETECTIONS_URL); - }); + waitForAlertsPanelToBeLoaded(); + waitForAlertsIndexToBeCreated(); + createCustomRuleActivated(newRule); + refreshPage(); + waitForAlertsToPopulate(); + selectNumberOfAlerts(5); + + cy.get(SELECTED_ALERTS).should('have.text', `Selected 5 alerts`); - afterEach(() => { - esArchiverUnload('closed_alerts'); + closeAlerts(); + waitForAlerts(); + refreshPage(); }); it('Open one alert when more than one closed alerts are selected', () => { - waitForAlerts(); - goToClosedAlerts(); - waitForAlertsToBeLoaded(); + waitForAlertsToPopulate(); cy.get(ALERTS_COUNT) .invoke('text') - .then((numberOfAlerts) => { - const numberOfAlertsToBeOpened = 1; - const numberOfAlertsToBeSelected = 3; - - cy.get(TAKE_ACTION_POPOVER_BTN).should('have.attr', 'disabled'); - selectNumberOfAlerts(numberOfAlertsToBeSelected); - cy.get(TAKE_ACTION_POPOVER_BTN).should('not.have.attr', 'disabled'); - - openFirstAlert(); - cy.reload(); + .then((numberOfOpenedAlertsText) => { + const numberOfOpenedAlerts = parseInt(numberOfOpenedAlertsText, 10); goToClosedAlerts(); - waitForAlertsToBeLoaded(); - waitForAlerts(); - - const expectedNumberOfAlerts = +numberOfAlerts - numberOfAlertsToBeOpened; - cy.get(ALERTS_COUNT).should('have.text', expectedNumberOfAlerts.toString()); - cy.get(SHOWING_ALERTS).should( - 'have.text', - `Showing ${expectedNumberOfAlerts.toString()} alerts` - ); - - goToOpenedAlerts(); - waitForAlerts(); - - cy.get(ALERTS_COUNT).should('have.text', numberOfAlertsToBeOpened.toString()); - cy.get(SHOWING_ALERTS).should( - 'have.text', - `Showing ${numberOfAlertsToBeOpened.toString()} alert` - ); - cy.get(ALERTS).should('have.length', numberOfAlertsToBeOpened); + cy.get(ALERTS_COUNT) + .invoke('text') + .then((numberOfAlerts) => { + const numberOfAlertsToBeOpened = 1; + const numberOfAlertsToBeSelected = 3; + + cy.get(TAKE_ACTION_POPOVER_BTN).should('have.attr', 'disabled'); + selectNumberOfAlerts(numberOfAlertsToBeSelected); + cy.get(SELECTED_ALERTS).should( + 'have.text', + `Selected ${numberOfAlertsToBeSelected} alerts` + ); + + cy.get(TAKE_ACTION_POPOVER_BTN).should('not.have.attr', 'disabled'); + + openFirstAlert(); + waitForAlerts(); + + const expectedNumberOfAlerts = +numberOfAlerts - numberOfAlertsToBeOpened; + cy.get(ALERTS_COUNT).should('have.text', expectedNumberOfAlerts.toString()); + cy.get(SHOWING_ALERTS).should( + 'have.text', + `Showing ${expectedNumberOfAlerts.toString()} alerts` + ); + + goToOpenedAlerts(); + waitForAlerts(); + + cy.get(ALERTS_COUNT).should( + 'have.text', + (numberOfOpenedAlerts + numberOfAlertsToBeOpened).toString() + ); + cy.get(SHOWING_ALERTS).should( + 'have.text', + `Showing ${(numberOfOpenedAlerts + numberOfAlertsToBeOpened).toString()} alerts` + ); + }); }); }); }); @@ -217,20 +219,15 @@ describe('Alerts', () => { context('Marking alerts as in-progress', () => { beforeEach(() => { cleanKibana(); - removeSignalsIndex(); - esArchiverLoad('alerts'); loginAndWaitForPage(DETECTIONS_URL); - }); - - afterEach(() => { - esArchiverUnload('alerts'); - removeSignalsIndex(); + waitForAlertsPanelToBeLoaded(); + waitForAlertsIndexToBeCreated(); + createCustomRuleActivated(newRule); + refreshPage(); + waitForAlertsToPopulate(); }); it('Mark one alert in progress when more than one open alerts are selected', () => { - waitForAlerts(); - waitForAlertsToBeLoaded(); - cy.get(ALERTS_COUNT) .invoke('text') .then((numberOfAlerts) => { @@ -242,8 +239,6 @@ describe('Alerts', () => { cy.get(TAKE_ACTION_POPOVER_BTN).should('not.have.attr', 'disabled'); markInProgressFirstAlert(); - cy.reload(); - goToOpenedAlerts(); waitForAlertsToBeLoaded(); const expectedNumberOfAlerts = +numberOfAlerts - numberOfAlertsToBeMarkedInProgress; diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_callouts_readonly.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_callouts_readonly.spec.ts index fa48c0bc1abc6..4bf54963a5322 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_callouts_readonly.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_callouts_readonly.spec.ts @@ -16,7 +16,7 @@ import { } from '../tasks/login'; import { waitForAlertsIndexToBeCreated } from '../tasks/alerts'; import { goToRuleDetails } from '../tasks/alerts_detection_rules'; -import { createCustomRule, deleteCustomRule, removeSignalsIndex } from '../tasks/api_calls/rules'; +import { createCustomRule, deleteCustomRule } from '../tasks/api_calls/rules'; import { getCallOut, waitForCallOutToBeShown, dismissCallOut } from '../tasks/common/callouts'; import { cleanKibana } from '../tasks/common'; @@ -42,7 +42,6 @@ describe('Detections > Callouts indicating read-only access to resources', () => // First, we have to open the app on behalf of a priviledged user in order to initialize it. // Otherwise the app will be disabled and show a "welcome"-like page. cleanKibana(); - removeSignalsIndex(); loginAndWaitForPageWithoutDateRange(DETECTIONS_URL, ROLES.platform_engineer); waitForAlertsIndexToBeCreated(); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_exceptions.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_exceptions.spec.ts index 265f4d43c71c1..44519adc25552 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_exceptions.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_exceptions.spec.ts @@ -16,7 +16,7 @@ import { goToOpenedAlerts, waitForAlertsIndexToBeCreated, } from '../tasks/alerts'; -import { createCustomRule, removeSignalsIndex } from '../tasks/api_calls/rules'; +import { createCustomRule } from '../tasks/api_calls/rules'; import { goToRuleDetails } from '../tasks/alerts_detection_rules'; import { waitForAlertsToPopulate } from '../tasks/create_new_rule'; import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; @@ -35,11 +35,10 @@ import { refreshPage } from '../tasks/security_header'; import { DETECTIONS_URL } from '../urls/navigation'; import { cleanKibana } from '../tasks/common'; -describe.skip('Exceptions', () => { +describe('Exceptions', () => { const NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS = '1'; beforeEach(() => { cleanKibana(); - removeSignalsIndex(); loginAndWaitForPageWithoutDateRange(DETECTIONS_URL); waitForAlertsIndexToBeCreated(); createCustomRule(newRule); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules.spec.ts index 4284b05205c69..9eb2127acb446 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules.spec.ts @@ -4,13 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ import { - FIFTH_RULE, FIRST_RULE, RULE_NAME, RULE_SWITCH, SECOND_RULE, - SEVENTH_RULE, RULE_AUTO_REFRESH_IDLE_MODAL, + FOURTH_RULE, } from '../screens/alerts_detection_rules'; import { @@ -28,43 +27,45 @@ import { waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, waitForRuleToBeActivated, } from '../tasks/alerts_detection_rules'; -import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; import { DEFAULT_RULE_REFRESH_INTERVAL_VALUE } from '../../common/constants'; import { DETECTIONS_URL } from '../urls/navigation'; -import { removeSignalsIndex } from '../tasks/api_calls/rules'; +import { createCustomRule, removeSignalsIndex } from '../tasks/api_calls/rules'; import { cleanKibana } from '../tasks/common'; +import { existingRule, newOverrideRule, newRule, newThresholdRule } from '../objects/rule'; describe('Alerts detection rules', () => { - before(() => { + beforeEach(() => { cleanKibana(); removeSignalsIndex(); - esArchiverLoad('prebuilt_rules_loaded'); + loginAndWaitForPageWithoutDateRange(DETECTIONS_URL); + waitForAlertsPanelToBeLoaded(); + waitForAlertsIndexToBeCreated(); + createCustomRule(newRule, 'rule1'); + createCustomRule(existingRule, 'rule2'); + createCustomRule(newOverrideRule, 'rule3'); + createCustomRule(newThresholdRule, 'rule4'); }); after(() => { - esArchiverUnload('prebuilt_rules_loaded'); + cy.clock().invoke('restore'); }); it('Sorts by activated rules', () => { - loginAndWaitForPageWithoutDateRange(DETECTIONS_URL); - waitForAlertsPanelToBeLoaded(); - waitForAlertsIndexToBeCreated(); goToManageAlertsDetectionRules(); - waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded(); cy.get(RULE_NAME) - .eq(FIFTH_RULE) + .eq(SECOND_RULE) .invoke('text') - .then((fifthRuleName) => { - activateRule(FIFTH_RULE); + .then((secondInitialRuleName) => { + activateRule(SECOND_RULE); waitForRuleToBeActivated(); cy.get(RULE_NAME) - .eq(SEVENTH_RULE) + .eq(FOURTH_RULE) .invoke('text') - .then((seventhRuleName) => { - activateRule(SEVENTH_RULE); + .then((fourthInitialRuleName) => { + activateRule(FOURTH_RULE); waitForRuleToBeActivated(); sortByActivatedRules(); cy.get(RULE_NAME) @@ -76,8 +77,8 @@ describe('Alerts detection rules', () => { .invoke('text') .then((secondRuleName) => { const expectedRulesNames = `${firstRuleName} ${secondRuleName}`; - cy.wrap(expectedRulesNames).should('include', fifthRuleName); - cy.wrap(expectedRulesNames).should('include', seventhRuleName); + cy.wrap(expectedRulesNames).should('include', secondInitialRuleName); + cy.wrap(expectedRulesNames).should('include', fourthInitialRuleName); }); }); cy.get(RULE_SWITCH).eq(FIRST_RULE).should('have.attr', 'role', 'switch'); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts index fb196fde3ae83..897f035d23b10 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts @@ -5,7 +5,7 @@ */ import { formatMitreAttackDescription } from '../helpers/rules'; -import { newRule, existingRule, indexPatterns, editedRule } from '../objects/rule'; +import { newRule, existingRule, indexPatterns, editedRule, newOverrideRule } from '../objects/rule'; import { ALERT_RULE_METHOD, ALERT_RULE_NAME, @@ -84,7 +84,7 @@ import { waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, waitForRulesToBeLoaded, } from '../tasks/alerts_detection_rules'; -import { removeSignalsIndex } from '../tasks/api_calls/rules'; +import { createCustomRuleActivated } from '../tasks/api_calls/rules'; import { createTimeline } from '../tasks/api_calls/timelines'; import { cleanKibana } from '../tasks/common'; import { @@ -100,8 +100,8 @@ import { waitForTheRuleToBeExecuted, } from '../tasks/create_new_rule'; import { saveEditedRule, waitForKibana } from '../tasks/edit_rule'; -import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; +import { refreshPage } from '../tasks/security_header'; import { DETECTIONS_URL } from '../urls/navigation'; @@ -114,9 +114,8 @@ describe('Custom detection rules creation', () => { const rule = { ...newRule }; - before(() => { + beforeEach(() => { cleanKibana(); - removeSignalsIndex(); createTimeline(newRule.timeline).then((response) => { rule.timeline.id = response.body.data.persistTimeline.timeline.savedObjectId; }); @@ -214,21 +213,19 @@ describe('Custom detection rules creation', () => { }); describe.skip('Custom detection rules deletion and edition', () => { - beforeEach(() => { - cleanKibana(); - removeSignalsIndex(); - esArchiverLoad('custom_rules'); - loginAndWaitForPageWithoutDateRange(DETECTIONS_URL); - waitForAlertsPanelToBeLoaded(); - waitForAlertsIndexToBeCreated(); - goToManageAlertsDetectionRules(); - }); - - afterEach(() => { - esArchiverUnload('custom_rules'); - }); - context('Deletion', () => { + beforeEach(() => { + cleanKibana(); + loginAndWaitForPageWithoutDateRange(DETECTIONS_URL); + goToManageAlertsDetectionRules(); + waitForAlertsIndexToBeCreated(); + createCustomRuleActivated(newRule, 'rule1'); + createCustomRuleActivated(newOverrideRule, 'rule2'); + createCustomRuleActivated(existingRule, 'rule3'); + refreshPage(); + goToManageAlertsDetectionRules(); + }); + it('Deletes one rule', () => { cy.get(RULES_TABLE) .find(RULES_ROW) @@ -263,7 +260,7 @@ describe.skip('Custom detection rules deletion and edition', () => { .find(RULES_ROW) .then((rules) => { const initialNumberOfRules = rules.length; - const numberOfRulesToBeDeleted = 3; + const numberOfRulesToBeDeleted = 2; const expectedNumberOfRulesAfterDeletion = initialNumberOfRules - numberOfRulesToBeDeleted; @@ -294,6 +291,16 @@ describe.skip('Custom detection rules deletion and edition', () => { const expectedEditedIndexPatterns = editedRule.index && editedRule.index.length ? editedRule.index : indexPatterns; + beforeEach(() => { + cleanKibana(); + loginAndWaitForPageWithoutDateRange(DETECTIONS_URL); + goToManageAlertsDetectionRules(); + waitForAlertsIndexToBeCreated(); + createCustomRuleActivated(existingRule, 'rule1'); + refreshPage(); + goToManageAlertsDetectionRules(); + }); + it('Allows a rule to be edited', () => { editFirstRule(); waitForKibana(); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts index d02c015a5f1f7..6567ee07c4e3a 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts @@ -62,7 +62,6 @@ import { waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, waitForRulesToBeLoaded, } from '../tasks/alerts_detection_rules'; -import { removeSignalsIndex } from '../tasks/api_calls/rules'; import { createTimeline } from '../tasks/api_calls/timelines'; import { cleanKibana } from '../tasks/common'; import { @@ -88,9 +87,8 @@ describe.skip('Detection rules, EQL', () => { const rule = { ...eqlRule }; - before(() => { + beforeEach(() => { cleanKibana(); - removeSignalsIndex(); createTimeline(eqlRule.timeline).then((response) => { rule.timeline.id = response.body.data.persistTimeline.timeline.savedObjectId; }); @@ -180,9 +178,8 @@ describe.skip('Detection rules, sequence EQL', () => { const expectedNumberOfSequenceAlerts = 1; const rule = { ...eqlSequenceRule }; - before(() => { + beforeEach(() => { cleanKibana(); - removeSignalsIndex(); createTimeline(eqlSequenceRule.timeline).then((response) => { rule.timeline.id = response.body.data.persistTimeline.timeline.savedObjectId; }); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_export.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_export.spec.ts index a9c1f7c331d0e..0f5ce9c47a439 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_export.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_export.spec.ts @@ -11,7 +11,7 @@ import { waitForAlertsPanelToBeLoaded, } from '../tasks/alerts'; import { exportFirstRule } from '../tasks/alerts_detection_rules'; -import { createCustomRule, removeSignalsIndex } from '../tasks/api_calls/rules'; +import { createCustomRule } from '../tasks/api_calls/rules'; import { cleanKibana } from '../tasks/common'; import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; @@ -19,9 +19,8 @@ import { DETECTIONS_URL } from '../urls/navigation'; describe.skip('Export rules', () => { let ruleResponse: Cypress.Response; - before(() => { + beforeEach(() => { cleanKibana(); - removeSignalsIndex(); cy.intercept( 'POST', '/api/detection_engine/rules/_export?exclude_export_details=false&file_name=rules_export.ndjson' diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_indicator_match.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_indicator_match.spec.ts index 4e97b619fc274..1f2793abcbf1f 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_indicator_match.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_indicator_match.spec.ts @@ -65,7 +65,6 @@ import { waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, waitForRulesToBeLoaded, } from '../tasks/alerts_detection_rules'; -import { removeSignalsIndex } from '../tasks/api_calls/rules'; import { cleanKibana } from '../tasks/common'; import { createAndActivateRule, @@ -91,7 +90,6 @@ describe('Detection rules, Indicator Match', () => { beforeEach(() => { cleanKibana(); - removeSignalsIndex(); esArchiverLoad('threat_indicator'); esArchiverLoad('threat_data'); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts index c651139248e0c..baefcba945447 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts @@ -52,7 +52,6 @@ import { waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, waitForRulesToBeLoaded, } from '../tasks/alerts_detection_rules'; -import { removeSignalsIndex } from '../tasks/api_calls/rules'; import { cleanKibana } from '../tasks/common'; import { createAndActivateRule, @@ -72,9 +71,8 @@ describe.skip('Detection rules, machine learning', () => { const expectedMitre = formatMitreAttackDescription(machineLearningRule.mitre); const expectedNumberOfRules = 1; - before(() => { + beforeEach(() => { cleanKibana(); - removeSignalsIndex(); }); it('Creates and activates a new ml rule', () => { diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts index a543dca00b010..c641d572f515c 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts @@ -69,7 +69,6 @@ import { waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, waitForRulesToBeLoaded, } from '../tasks/alerts_detection_rules'; -import { removeSignalsIndex } from '../tasks/api_calls/rules'; import { createTimeline } from '../tasks/api_calls/timelines'; import { cleanKibana } from '../tasks/common'; import { @@ -84,9 +83,7 @@ import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; import { DETECTIONS_URL } from '../urls/navigation'; -// FLAKY: https://github.com/elastic/kibana/issues/85671 -// FLAKY: https://github.com/elastic/kibana/issues/84020 -describe.skip('Detection rules, override', () => { +describe('Detection rules, override', () => { const expectedUrls = newOverrideRule.referenceUrls.join(''); const expectedFalsePositives = newOverrideRule.falsePositivesExamples.join(''); const expectedTags = newOverrideRule.tags.join(''); @@ -96,7 +93,6 @@ describe.skip('Detection rules, override', () => { beforeEach(() => { cleanKibana(); - removeSignalsIndex(); createTimeline(newOverrideRule.timeline).then((response) => { rule.timeline.id = response.body.data.persistTimeline.timeline.savedObjectId; }); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_prebuilt.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_prebuilt.spec.ts index a4e41631ea246..4d2efc47db483 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_prebuilt.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_prebuilt.spec.ts @@ -30,20 +30,16 @@ import { waitForPrebuiltDetectionRulesToBeLoaded, waitForRulesToBeLoaded, } from '../tasks/alerts_detection_rules'; -import { esArchiverLoadEmptyKibana } from '../tasks/es_archiver'; import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; import { DETECTIONS_URL } from '../urls/navigation'; import { totalNumberOfPrebuiltRules } from '../objects/rule'; -import { removeSignalsIndex } from '../tasks/api_calls/rules'; import { cleanKibana } from '../tasks/common'; -describe.skip('Alerts rules, prebuilt rules', () => { - before(() => { +describe('Alerts rules, prebuilt rules', () => { + beforeEach(() => { cleanKibana(); - removeSignalsIndex(); - esArchiverLoadEmptyKibana(); }); it('Loads prebuilt rules', () => { @@ -84,7 +80,6 @@ describe('Deleting prebuilt rules', () => { const expectedElasticRulesBtnText = `Elastic rules (${expectedNumberOfRules})`; cleanKibana(); - esArchiverLoadEmptyKibana(); loginAndWaitForPageWithoutDateRange(DETECTIONS_URL); waitForAlertsPanelToBeLoaded(); waitForAlertsIndexToBeCreated(); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts index 812d0fa29f9b7..058bac6258ffc 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts @@ -64,7 +64,6 @@ import { waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, waitForRulesToBeLoaded, } from '../tasks/alerts_detection_rules'; -import { removeSignalsIndex } from '../tasks/api_calls/rules'; import { createTimeline } from '../tasks/api_calls/timelines'; import { cleanKibana } from '../tasks/common'; import { @@ -90,7 +89,6 @@ describe.skip('Detection rules, threshold', () => { beforeEach(() => { cleanKibana(); - removeSignalsIndex(); createTimeline(newThresholdRule.timeline).then((response) => { rule.timeline.id = response.body.data.persistTimeline.timeline.savedObjectId; }); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_timeline.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_timeline.spec.ts index d5fba65a70149..e42410f7fb38d 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_timeline.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_timeline.spec.ts @@ -4,30 +4,34 @@ * you may not use this file except in compliance with the Elastic License. */ +import { newRule } from '../objects/rule'; import { PROVIDER_BADGE } from '../screens/timeline'; -import { investigateFirstAlertInTimeline, waitForAlertsPanelToBeLoaded } from '../tasks/alerts'; -import { removeSignalsIndex } from '../tasks/api_calls/rules'; +import { + investigateFirstAlertInTimeline, + waitForAlertsIndexToBeCreated, + waitForAlertsPanelToBeLoaded, +} from '../tasks/alerts'; +import { createCustomRuleActivated } from '../tasks/api_calls/rules'; import { cleanKibana } from '../tasks/common'; -import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; +import { waitForAlertsToPopulate } from '../tasks/create_new_rule'; import { loginAndWaitForPage } from '../tasks/login'; +import { refreshPage } from '../tasks/security_header'; import { DETECTIONS_URL } from '../urls/navigation'; describe('Alerts timeline', () => { beforeEach(() => { cleanKibana(); - removeSignalsIndex(); - esArchiverLoad('timeline_alerts'); loginAndWaitForPage(DETECTIONS_URL); - }); - - afterEach(() => { - esArchiverUnload('timeline_alerts'); + waitForAlertsPanelToBeLoaded(); + waitForAlertsIndexToBeCreated(); + createCustomRuleActivated(newRule); + refreshPage(); + waitForAlertsToPopulate(); }); it('Investigate alert in default timeline', () => { - waitForAlertsPanelToBeLoaded(); investigateFirstAlertInTimeline(); cy.get(PROVIDER_BADGE) .first() diff --git a/x-pack/plugins/security_solution/cypress/integration/cases.spec.ts b/x-pack/plugins/security_solution/cypress/integration/cases.spec.ts index d53b98b6c103d..18325401d9abc 100644 --- a/x-pack/plugins/security_solution/cypress/integration/cases.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/cases.spec.ts @@ -51,10 +51,10 @@ import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; import { CASES_URL } from '../urls/navigation'; -describe.skip('Cases', () => { +describe('Cases', () => { const mycase = { ...case1 }; - before(() => { + beforeEach(() => { cleanKibana(); createTimeline(case1.timeline).then((response) => { mycase.timeline.id = response.body.data.persistTimeline.timeline.savedObjectId; diff --git a/x-pack/plugins/security_solution/cypress/integration/cases_connector_options.spec.ts b/x-pack/plugins/security_solution/cypress/integration/cases_connector_options.spec.ts index c41b79ef33653..e8fd69864cb3e 100644 --- a/x-pack/plugins/security_solution/cypress/integration/cases_connector_options.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/cases_connector_options.spec.ts @@ -27,7 +27,7 @@ import { CONNECTOR_CARD_DETAILS, CONNECTOR_TITLE } from '../screens/case_details import { cleanKibana } from '../tasks/common'; describe('Cases connector incident fields', () => { - before(() => { + beforeEach(() => { cleanKibana(); cy.intercept('GET', '/api/cases/configure/connectors/_find', mockConnectorsResponse); cy.intercept('POST', `/api/actions/action/${connectorIds.jira}/_execute`, (req) => { diff --git a/x-pack/plugins/security_solution/cypress/integration/cases_connectors.spec.ts b/x-pack/plugins/security_solution/cypress/integration/cases_connectors.spec.ts index 8bd9f5b09f2c8..9e39a210c1113 100644 --- a/x-pack/plugins/security_solution/cypress/integration/cases_connectors.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/cases_connectors.spec.ts @@ -38,7 +38,7 @@ describe('Cases connectors', () => { ], version: 'WzEwNCwxXQ==', }; - before(() => { + beforeEach(() => { cleanKibana(); cy.intercept('POST', '/api/actions/action').as('createConnector'); cy.intercept('POST', '/api/cases/configure', (req) => { diff --git a/x-pack/plugins/security_solution/cypress/integration/events_viewer.spec.ts b/x-pack/plugins/security_solution/cypress/integration/events_viewer.spec.ts index f7a19fa281bee..4e34dcac1873d 100644 --- a/x-pack/plugins/security_solution/cypress/integration/events_viewer.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/events_viewer.spec.ts @@ -23,7 +23,6 @@ import { openEvents } from '../tasks/hosts/main'; import { addsHostGeoCityNameToHeader, addsHostGeoCountryNameToHeader, - closeModal, dragAndDropColumn, openEventsViewerFieldsBrowser, opensInspectQueryModal, @@ -63,7 +62,7 @@ describe.skip('Events Viewer', () => { }); it('displays the `default ECS` category (by default)', () => { - cy.get(FIELDS_BROWSER_SELECTED_CATEGORY_TITLE).invoke('text').should('eq', 'default ECS'); + cy.get(FIELDS_BROWSER_SELECTED_CATEGORY_TITLE).should('have.text', 'default ECS'); }); it('displays a checked checkbox for all of the default events viewer columns that are also in the default ECS category', () => { @@ -80,11 +79,6 @@ describe.skip('Events Viewer', () => { openEvents(); }); - after(() => { - closeModal(); - cy.get(INSPECT_MODAL).should('not.exist'); - }); - it('launches the inspect query modal when the inspect button is clicked', () => { waitsForEventsToBeLoaded(); opensInspectQueryModal(); @@ -142,7 +136,7 @@ describe.skip('Events Viewer', () => { .invoke('text') .then((initialNumberOfEvents) => { kqlSearch(`${filterInput}{enter}`); - cy.get(HEADER_SUBTITLE).invoke('text').should('not.equal', initialNumberOfEvents); + cy.get(HEADER_SUBTITLE).should('not.have.text', initialNumberOfEvents); }); }); }); @@ -167,9 +161,9 @@ describe.skip('Events Viewer', () => { const expectedOrderAfterDragAndDrop = 'message@timestamp1host.nameevent.moduleevent.datasetevent.actionuser.namesource.ipdestination.ip'; - cy.get(HEADERS_GROUP).invoke('text').should('equal', originalColumnOrder); + cy.get(HEADERS_GROUP).should('have.text', originalColumnOrder); dragAndDropColumn({ column: 0, newPosition: 0 }); - cy.get(HEADERS_GROUP).invoke('text').should('equal', expectedOrderAfterDragAndDrop); + cy.get(HEADERS_GROUP).should('have.text', expectedOrderAfterDragAndDrop); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/fields_browser.spec.ts b/x-pack/plugins/security_solution/cypress/integration/fields_browser.spec.ts index d99981b42d049..55ded8014db3c 100644 --- a/x-pack/plugins/security_solution/cypress/integration/fields_browser.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/fields_browser.spec.ts @@ -60,13 +60,14 @@ describe('Fields Browser', () => { }); it('displays the `default ECS` category (by default)', () => { - cy.get(FIELDS_BROWSER_SELECTED_CATEGORY_TITLE).invoke('text').should('eq', 'default ECS'); + cy.get(FIELDS_BROWSER_SELECTED_CATEGORY_TITLE).should('have.text', 'default ECS'); }); it('the `defaultECS` (selected) category count matches the default timeline header count', () => { - cy.get(FIELDS_BROWSER_SELECTED_CATEGORY_COUNT) - .invoke('text') - .should('eq', `${defaultHeaders.length}`); + cy.get(FIELDS_BROWSER_SELECTED_CATEGORY_COUNT).should( + 'have.text', + `${defaultHeaders.length}` + ); }); it('displays a checked checkbox for all of the default timeline columns', () => { @@ -80,7 +81,7 @@ describe('Fields Browser', () => { filterFieldsBrowser(filterInput); - cy.get(FIELDS_BROWSER_CATEGORIES_COUNT).invoke('text').should('eq', '2 categories'); + cy.get(FIELDS_BROWSER_CATEGORIES_COUNT).should('have.text', '2 categories'); }); it('displays a search results label with the expected count of fields matching the filter input', () => { @@ -94,9 +95,10 @@ describe('Fields Browser', () => { cy.get(FIELDS_BROWSER_SYSTEM_CATEGORIES_COUNT) .invoke('text') .then((systemCategoriesCount) => { - cy.get(FIELDS_BROWSER_FIELDS_COUNT) - .invoke('text') - .should('eq', `${+hostCategoriesCount + +systemCategoriesCount} fields`); + cy.get(FIELDS_BROWSER_FIELDS_COUNT).should( + 'have.text', + `${+hostCategoriesCount + +systemCategoriesCount} fields` + ); }); }); }); @@ -106,11 +108,11 @@ describe('Fields Browser', () => { filterFieldsBrowser(filterInput); - cy.get(FIELDS_BROWSER_SELECTED_CATEGORY_COUNT).invoke('text').should('eq', '4'); + cy.get(FIELDS_BROWSER_SELECTED_CATEGORY_COUNT).should('have.text', '4'); }); }); - context.skip('Editing the timeline', () => { + context('Editing the timeline', () => { before(() => { cleanKibana(); loginAndWaitForPage(HOSTS_URL); @@ -137,7 +139,7 @@ describe('Fields Browser', () => { const category = 'host'; filterFieldsBrowser(category); - cy.get(FIELDS_BROWSER_SELECTED_CATEGORY_TITLE).invoke('text').should('eq', category); + cy.get(FIELDS_BROWSER_SELECTED_CATEGORY_TITLE).should('have.text', category); }); it('adds a field to the timeline when the user clicks the checkbox', () => { @@ -151,7 +153,7 @@ describe('Fields Browser', () => { cy.get(FIELDS_BROWSER_HOST_GEO_CITY_NAME_HEADER).should('exist'); }); - it('adds a field to the timeline when the user drags and drops a field', () => { + it.skip('adds a field to the timeline when the user drags and drops a field', () => { const filterInput = 'host.geo.c'; filterFieldsBrowser(filterInput); diff --git a/x-pack/plugins/security_solution/cypress/integration/inspect.spec.ts b/x-pack/plugins/security_solution/cypress/integration/inspect.spec.ts index 6321be1e26151..98891e65771ce 100644 --- a/x-pack/plugins/security_solution/cypress/integration/inspect.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/inspect.spec.ts @@ -18,7 +18,7 @@ import { executeTimelineKQL, openTimelineInspectButton } from '../tasks/timeline import { HOSTS_URL, NETWORK_URL } from '../urls/navigation'; -describe('Inspect', () => { +describe.skip('Inspect', () => { context('Hosts stats and tables', () => { before(() => { cleanKibana(); diff --git a/x-pack/plugins/security_solution/cypress/integration/pagination.spec.ts b/x-pack/plugins/security_solution/cypress/integration/pagination.spec.ts index 2896b2dbc36c6..95cbf8220402f 100644 --- a/x-pack/plugins/security_solution/cypress/integration/pagination.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/pagination.spec.ts @@ -17,7 +17,7 @@ import { refreshPage } from '../tasks/security_header'; import { HOSTS_PAGE_TAB_URLS } from '../urls/navigation'; -describe('Pagination', () => { +describe.skip('Pagination', () => { before(() => { cleanKibana(); loginAndWaitForPage(HOSTS_PAGE_TAB_URLS.uncommonProcesses); diff --git a/x-pack/plugins/security_solution/cypress/integration/search_bar.spec.ts b/x-pack/plugins/security_solution/cypress/integration/search_bar.spec.ts index 7fcbc10f88b44..e5e74f6eb0cac 100644 --- a/x-pack/plugins/security_solution/cypress/integration/search_bar.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/search_bar.spec.ts @@ -13,7 +13,7 @@ import { HOSTS_URL } from '../urls/navigation'; import { waitForAllHostsToBeLoaded } from '../tasks/hosts/all_hosts'; import { cleanKibana } from '../tasks/common'; -describe('SearchBar', () => { +describe.skip('SearchBar', () => { before(() => { cleanKibana(); loginAndWaitForPage(HOSTS_URL); diff --git a/x-pack/plugins/security_solution/cypress/integration/sourcerer.spec.ts b/x-pack/plugins/security_solution/cypress/integration/sourcerer.spec.ts index b441d33d34baf..aa126e2f33c90 100644 --- a/x-pack/plugins/security_solution/cypress/integration/sourcerer.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/sourcerer.spec.ts @@ -28,7 +28,7 @@ import { populateTimeline } from '../tasks/timeline'; import { SERVER_SIDE_EVENT_COUNT } from '../screens/timeline'; import { cleanKibana } from '../tasks/common'; -describe('Sourcerer', () => { +describe.skip('Sourcerer', () => { before(() => { cleanKibana(); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_attach_to_case.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_attach_to_case.spec.ts index 74bf4f03b0b14..a0051eee0a22e 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timeline_attach_to_case.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timeline_attach_to_case.spec.ts @@ -12,13 +12,14 @@ import { selectCase, } from '../tasks/timeline'; import { DESCRIPTION_INPUT, ADD_COMMENT_INPUT } from '../screens/create_new_case'; -import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; -import { TIMELINE_CASE_ID } from '../objects/case'; -import { caseTimeline, timeline } from '../objects/timeline'; -import { createTimeline, deleteTimeline } from '../tasks/api_calls/timelines'; +import { case1 } from '../objects/case'; +import { timeline } from '../objects/timeline'; +import { createTimeline } from '../tasks/api_calls/timelines'; import { cleanKibana } from '../tasks/common'; +import { createCase } from '../tasks/api_calls/cases'; -describe('attach timeline to case', () => { +// https://github.com/elastic/kibana/issues/86959 +describe.skip('attach timeline to case', () => { const myTimeline = { ...timeline }; context('without cases created', () => { @@ -29,10 +30,6 @@ describe('attach timeline to case', () => { }); }); - after(() => { - deleteTimeline(myTimeline.id!); - }); - it('attach timeline to a new case', () => { loginAndWaitForTimeline(myTimeline.id!); attachTimelineToNewCase(); @@ -62,25 +59,29 @@ describe('attach timeline to case', () => { }); context('with cases created', () => { + let timelineId: string; + let caseId: string; before(() => { cleanKibana(); - esArchiverLoad('case_and_timeline'); + createTimeline(timeline).then((response) => { + timelineId = response.body.data.persistTimeline.timeline.savedObjectId; + }); + createCase(case1).then((response) => { + caseId = response.body.id; + }); }); it('attach timeline to an existing case', () => { - loginAndWaitForTimeline(caseTimeline.id!); + loginAndWaitForTimeline(timelineId); attachTimelineToExistingCase(); - selectCase(TIMELINE_CASE_ID); + selectCase(caseId); cy.location('origin').then((origin) => { cy.get(ADD_COMMENT_INPUT).should( 'have.text', - `[${ - caseTimeline.title - }](${origin}/app/security/timelines?timeline=(id:%27${caseTimeline.id!}%27,isOpen:!t))` + `[${timeline.title}](${origin}/app/security/timelines?timeline=(id:%27${timelineId}%27,isOpen:!t))` ); }); - esArchiverUnload('case_and_timeline'); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_creation.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_creation.spec.ts index 5d44c057c7383..a926a5ac4938a 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timeline_creation.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timeline_creation.spec.ts @@ -9,9 +9,9 @@ import { FAVORITE_TIMELINE, LOCKED_ICON, NOTES_TAB_BUTTON, + NOTES_TEXT, // NOTES_COUNT, NOTES_TEXT_AREA, - NOTE_CONTENT, PIN_EVENT, TIMELINE_DESCRIPTION, TIMELINE_FILTER, @@ -25,7 +25,6 @@ import { TIMELINES_NOTES_COUNT, TIMELINES_FAVORITE, } from '../screens/timelines'; -import { getTimelineById } from '../tasks/api_calls/timelines'; import { cleanKibana } from '../tasks/common'; import { loginAndWaitForPage } from '../tasks/login'; @@ -47,11 +46,10 @@ import { openTimeline } from '../tasks/timelines'; import { OVERVIEW_URL } from '../urls/navigation'; -// FLAKY: https://github.com/elastic/kibana/issues/79389 -describe.skip('Timelines', () => { +describe('Timelines', () => { let timelineId: string; - before(() => { + beforeEach(() => { cleanKibana(); }); @@ -98,15 +96,10 @@ describe.skip('Timelines', () => { cy.get(PIN_EVENT) .should('have.attr', 'aria-label') .and('match', /Unpin the event in row 2/); - cy.get(LOCKED_ICON).should('be.visible'); cy.get(NOTES_TAB_BUTTON).click(); cy.get(NOTES_TEXT_AREA).should('exist'); - getTimelineById(timelineId).then((singleTimeline) => { - const noteId = singleTimeline!.body.data.getOneTimeline.notes[0].noteId; - - cy.get(NOTE_CONTENT(noteId)).should('have.text', timeline.notes); - }); + cy.get(NOTES_TEXT).should('have.text', timeline.notes); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts index 8b84ae7815452..1d0256dbfbdc9 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts @@ -15,7 +15,7 @@ import { removeColumn } from '../tasks/timeline'; // Failing: See https://github.com/elastic/kibana/issues/75794 describe.skip('persistent timeline', () => { - before(() => { + beforeEach(() => { cleanKibana(); loginAndWaitForPage(HOSTS_URL); openEvents(); diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_template_creation.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_template_creation.spec.ts index f1aaa4ab8b980..5672a232e0485 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timeline_template_creation.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timeline_template_creation.spec.ts @@ -44,7 +44,7 @@ import { openTimeline } from '../tasks/timelines'; import { OVERVIEW_URL } from '../urls/navigation'; describe('Timeline Templates', () => { - before(() => { + beforeEach(() => { cleanKibana(); cy.intercept('PATCH', '/api/timeline').as('timeline'); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_templates_export.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_templates_export.spec.ts index 015c0fc80e292..f2af37c939d02 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timeline_templates_export.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timeline_templates_export.spec.ts @@ -19,7 +19,7 @@ describe('Export timelines', () => { let templateResponse: Cypress.Response; let templateId: string; - before(() => { + beforeEach(() => { cleanKibana(); cy.intercept('POST', 'api/timeline/_export?file_name=timelines_export.ndjson').as('export'); createTimelineTemplate(timelineTemplate).then((response) => { diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_toggle_column.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_toggle_column.spec.ts index 9a03936c3683f..705aff7b14c6c 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timeline_toggle_column.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timeline_toggle_column.spec.ts @@ -4,14 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { timeline } from '../objects/timeline'; import { ID_HEADER_FIELD, ID_TOGGLE_FIELD, TIMESTAMP_HEADER_FIELD, TIMESTAMP_TOGGLE_FIELD, } from '../screens/timeline'; -import { createTimeline } from '../tasks/api_calls/timelines'; import { cleanKibana } from '../tasks/common'; import { loginAndWaitForPage } from '../tasks/login'; @@ -28,13 +26,11 @@ import { import { HOSTS_URL } from '../urls/navigation'; -describe('toggle column in timeline', () => { +describe.skip('toggle column in timeline', () => { before(() => { cleanKibana(); cy.intercept('POST', '/api/timeline/_export?file_name=timelines_export.ndjson').as('export'); - createTimeline(timeline).then((response) => { - loginAndWaitForPage(HOSTS_URL); - }); + loginAndWaitForPage(HOSTS_URL); }); beforeEach(() => { diff --git a/x-pack/plugins/security_solution/cypress/integration/timelines_export.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timelines_export.spec.ts index 064d98bf01b24..a75074baeef54 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timelines_export.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timelines_export.spec.ts @@ -15,7 +15,7 @@ import { cleanKibana } from '../tasks/common'; describe('Export timelines', () => { let timelineResponse: Cypress.Response; let timelineId: string; - before(() => { + beforeEach(() => { cleanKibana(); cy.intercept('POST', '/api/timeline/_export?file_name=timelines_export.ndjson').as('export'); createTimeline(timeline).then((response) => { diff --git a/x-pack/plugins/security_solution/cypress/integration/url_compatibility.spec.ts b/x-pack/plugins/security_solution/cypress/integration/url_compatibility.spec.ts index 58ef4cd2d96ba..cf433891ac929 100644 --- a/x-pack/plugins/security_solution/cypress/integration/url_compatibility.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/url_compatibility.spec.ts @@ -19,7 +19,7 @@ const ABSOLUTE_DATE = { startTime: '2019-08-01T20:03:29.186Z', }; -describe('URL compatibility', () => { +describe.skip('URL compatibility', () => { before(() => { cleanKibana(); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/value_lists.spec.ts b/x-pack/plugins/security_solution/cypress/integration/value_lists.spec.ts index 0b1ab12f37c91..ae0c4f35177a9 100644 --- a/x-pack/plugins/security_solution/cypress/integration/value_lists.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/value_lists.spec.ts @@ -26,7 +26,6 @@ import { exportValueList, } from '../tasks/lists'; import { VALUE_LISTS_TABLE, VALUE_LISTS_ROW, VALUE_LISTS_MODAL_ACTIVATOR } from '../screens/lists'; -import { removeSignalsIndex } from '../tasks/api_calls/rules'; import { cleanKibana } from '../tasks/common'; describe('value lists', () => { @@ -36,7 +35,6 @@ describe('value lists', () => { }); beforeEach(() => { - removeSignalsIndex(); loginAndWaitForPageWithoutDateRange(DETECTIONS_URL); waitForAlertsPanelToBeLoaded(); waitForAlertsIndexToBeCreated(); @@ -46,7 +44,6 @@ describe('value lists', () => { }); afterEach(() => { - removeSignalsIndex(); deleteAllValueListsFromUI(); }); diff --git a/x-pack/plugins/security_solution/cypress/objects/rule.ts b/x-pack/plugins/security_solution/cypress/objects/rule.ts index d48ac26472c71..c4515379eaeb2 100644 --- a/x-pack/plugins/security_solution/cypress/objects/rule.ts +++ b/x-pack/plugins/security_solution/cypress/objects/rule.ts @@ -176,18 +176,11 @@ export const newRule: CustomRule = { }; export const existingRule: CustomRule = { - customQuery: 'host.name:*', + customQuery: 'host.name: *', name: 'Rule 1', description: 'Description for Rule 1', - index: [ - 'apm-*-transaction*', - 'auditbeat-*', - 'endgame-*', - 'filebeat-*', - 'packetbeat-*', - 'winlogbeat-*', - ], - interval: '4m', + index: ['auditbeat-*'], + interval: '10s', severity: 'High', riskScore: '19', tags: ['rule1'], @@ -203,7 +196,7 @@ export const existingRule: CustomRule = { export const newOverrideRule: OverrideRule = { customQuery: 'host.name: *', index: indexPatterns, - name: 'New Rule Test', + name: 'Override Rule', description: 'The new rule description.', severity: 'High', riskScore: '17', @@ -224,7 +217,7 @@ export const newOverrideRule: OverrideRule = { export const newThresholdRule: ThresholdRule = { customQuery: 'host.name: *', index: indexPatterns, - name: 'New Rule Test', + name: 'Threshold Rule', description: 'The new rule description.', severity: 'High', riskScore: '17', diff --git a/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts b/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts index 5ac8cd8f6cc9f..d13102620ec19 100644 --- a/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts +++ b/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts @@ -26,6 +26,8 @@ export const FIFTH_RULE = 4; export const FIRST_RULE = 0; +export const FOURTH_RULE = 3; + export const LOAD_PREBUILT_RULES_BTN = '[data-test-subj="load-prebuilt-rules"]'; export const LOADING_INITIAL_PREBUILT_RULES_TABLE = diff --git a/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts b/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts index 17567b61ad314..9db30a174ae08 100644 --- a/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts +++ b/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts @@ -33,8 +33,7 @@ export const COMBO_BOX_RESULT = '.euiFilterSelectItem'; export const CREATE_AND_ACTIVATE_BTN = '[data-test-subj="create-activate"]'; -export const CUSTOM_QUERY_INPUT = - '[data-test-subj="detectionEngineStepDefineRuleQueryBar"] [data-test-subj="queryInput"]'; +export const CUSTOM_QUERY_INPUT = '[data-test-subj="queryInput"]'; export const THREAT_MATCH_QUERY_INPUT = '[data-test-subj="detectionEngineStepDefineThreatRuleQueryBar"] [data-test-subj="queryInput"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/timeline.ts b/x-pack/plugins/security_solution/cypress/screens/timeline.ts index 6f31a470dd61e..ea3c42e2650eb 100644 --- a/x-pack/plugins/security_solution/cypress/screens/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/screens/timeline.ts @@ -53,7 +53,7 @@ export const LOCKED_ICON = '[data-test-subj="timeline-date-picker-lock-button"]' export const NOTES = '[data-test-subj="note-card-body"]'; -const NOTE_BY_NOTE_ID = (noteId: string) => `[data-test-subj="note-preview-${noteId}"]`; +export const NOTE_BY_NOTE_ID = (noteId: string) => `[data-test-subj="note-preview-${noteId}"]`; export const NOTE_CONTENT = (noteId: string) => `${NOTE_BY_NOTE_ID(noteId)} p`; @@ -61,6 +61,8 @@ export const NOTES_TEXT_AREA = '[data-test-subj="add-a-note"] textarea'; export const NOTES_TAB_BUTTON = '[data-test-subj="timelineTabs-notes"]'; +export const NOTES_TEXT = '.euiMarkdownFormat'; + export const NOTES_COUNT = '[data-test-subj="timeline-notes-count"]'; export const OPEN_TIMELINE_ICON = '[data-test-subj="open-timeline-button"]'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/cases.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/cases.ts new file mode 100644 index 0000000000000..4510ebf254ee7 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/cases.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { TestCase } from '../../objects/case'; + +export const createCase = (newCase: TestCase) => + cy.request({ + method: 'POST', + url: 'api/cases', + body: { + description: newCase.description, + title: newCase.name, + tags: ['tag'], + connector: { + id: 'none', + name: 'none', + type: '.none', + fields: null, + }, + settings: { + syncAlerts: true, + }, + }, + headers: { 'kbn-xsrf': 'cypress-creds' }, + }); diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts index 34fc00428d2cd..29cdf4ec2be5d 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts @@ -6,12 +6,12 @@ import { CustomRule } from '../../objects/rule'; -export const createCustomRule = (rule: CustomRule) => +export const createCustomRule = (rule: CustomRule, ruleId = 'rule_testing') => cy.request({ method: 'POST', url: 'api/detection_engine/rules', body: { - rule_id: 'rule_testing', + rule_id: ruleId, risk_score: parseInt(rule.riskScore, 10), description: rule.description, interval: '10s', @@ -27,11 +27,34 @@ export const createCustomRule = (rule: CustomRule) => headers: { 'kbn-xsrf': 'cypress-creds' }, }); -export const deleteCustomRule = () => { +export const createCustomRuleActivated = (rule: CustomRule, ruleId = 'rule_testing') => + cy.request({ + method: 'POST', + url: 'api/detection_engine/rules', + body: { + rule_id: ruleId, + risk_score: parseInt(rule.riskScore, 10), + description: rule.description, + interval: '10s', + name: rule.name, + severity: rule.severity.toLocaleLowerCase(), + type: 'query', + from: 'now-17520h', + index: ['auditbeat-*'], + query: rule.customQuery, + language: 'kuery', + enabled: true, + tags: ['rule1'], + }, + headers: { 'kbn-xsrf': 'cypress-creds' }, + }); + +export const deleteCustomRule = (ruleId = 'rule_testing') => { cy.request({ method: 'DELETE', - url: 'api/detection_engine/rules?rule_id=rule_testing', + url: `api/detection_engine/rules?rule_id=${ruleId}`, headers: { 'kbn-xsrf': 'cypress-creds' }, + failOnStatusCode: false, }); }; diff --git a/x-pack/plugins/security_solution/cypress/tasks/common.ts b/x-pack/plugins/security_solution/cypress/tasks/common.ts index fbd4c4145e8ff..b6625a76981e8 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/common.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/common.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { removeSignalsIndex } from './api_calls/rules'; import { esArchiverLoadEmptyKibana } from './es_archiver'; const primaryButton = 0; @@ -65,5 +66,23 @@ export const reload = (afterReload: () => void) => { export const cleanKibana = () => { cy.exec(`curl -XDELETE "${Cypress.env('ELASTICSEARCH_URL')}/.kibana\*" -k`); + + // We wait until the kibana indexes are deleted + cy.waitUntil(() => { + cy.wait(500); + return cy + .request(`${Cypress.env('ELASTICSEARCH_URL')}/.kibana\*`) + .then((response) => JSON.stringify(response.body) === '{}'); + }); esArchiverLoadEmptyKibana(); + + // We wait until the kibana indexes are created + cy.waitUntil(() => { + cy.wait(500); + return cy + .request(`${Cypress.env('ELASTICSEARCH_URL')}/.kibana\*`) + .then((response) => JSON.stringify(response.body) !== '{}'); + }); + + removeSignalsIndex(); }; diff --git a/x-pack/plugins/security_solution/cypress/tasks/hosts/events.ts b/x-pack/plugins/security_solution/cypress/tasks/hosts/events.ts index 401a78767ac57..3e6b0ec0afaaa 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/hosts/events.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/hosts/events.ts @@ -42,7 +42,7 @@ export const loadMoreEvents = () => { export const openEventsViewerFieldsBrowser = () => { cy.get(EVENTS_VIEWER_FIELDS_BUTTON).click({ force: true }); - cy.get(SERVER_SIDE_EVENT_COUNT).invoke('text').should('not.equal', '0'); + cy.get(SERVER_SIDE_EVENT_COUNT).should('not.have.text', '0'); cy.get(FIELDS_BROWSER_CONTAINER).should('exist'); }; diff --git a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts index fee1bc4ae6892..0361bf4b72b52 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts @@ -166,12 +166,7 @@ export const pinFirstEvent = () => { export const populateTimeline = () => { executeTimelineKQL(hostExistsQuery); - cy.get(SERVER_SIDE_EVENT_COUNT) - .invoke('text') - .then((strCount) => { - const intCount = +strCount; - cy.wrap(intCount).should('be.above', 0); - }); + cy.get(SERVER_SIDE_EVENT_COUNT).should('not.have.text', '0'); }; export const unpinFirstEvent = () => { diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.test.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.test.tsx index 0c9a725f918e5..a92fc793c796e 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.test.tsx @@ -8,11 +8,11 @@ import React from 'react'; import { mount } from 'enzyme'; import { CaseStatuses } from '../../../../../case/common/api'; -import { CasesTableFilters } from './table_filters'; import { TestProviders } from '../../../common/mock'; import { useGetTags } from '../../containers/use_get_tags'; import { useGetReporters } from '../../containers/use_get_reporters'; import { DEFAULT_FILTER_OPTIONS } from '../../containers/use_get_cases'; +import { CasesTableFilters } from './table_filters'; jest.mock('../../containers/use_get_reporters'); jest.mock('../../containers/use_get_tags'); @@ -151,4 +151,20 @@ describe('CasesTableFilters ', () => { ); expect(onFilterChanged).toHaveBeenCalledWith({ reporters: [{ username: 'casetester' }] }); }); + + it('StatusFilterWrapper should have a fixed width of 180px', () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="status-filter-wrapper"]').first()).toHaveStyleRule( + 'flex-basis', + '180px', + { + modifier: '&&', + } + ); + }); }); diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.tsx index f5ec0bf144154..768ad300c02e6 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.tsx @@ -6,6 +6,7 @@ import React, { useCallback, useEffect, useState, useMemo } from 'react'; import { isEqual } from 'lodash/fp'; +import styled from 'styled-components'; import { EuiFlexGroup, EuiFlexItem, EuiFieldSearch, EuiFilterGroup } from '@elastic/eui'; import { CaseStatuses } from '../../../../../case/common/api'; @@ -25,6 +26,13 @@ interface CasesTableFiltersProps { setFilterRefetch: (val: () => void) => void; } +// Fix the width of the status dropdown to prevent hiding long text items +const StatusFilterWrapper = styled(EuiFlexItem)` + && { + flex-basis: 180px; + } +`; + /** * Collection of filters for filtering data within the CasesTable. Contains search bar, * and tag selection @@ -131,23 +139,27 @@ const CasesTableFiltersComponent = ({ ); return ( - - - - - - + + + + + + + + + + diff --git a/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/auto_download.test.tsx b/x-pack/plugins/security_solution/public/common/components/auto_download/auto_download.test.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/auto_download.test.tsx rename to x-pack/plugins/security_solution/public/common/components/auto_download/auto_download.test.tsx diff --git a/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/auto_download.tsx b/x-pack/plugins/security_solution/public/common/components/auto_download/auto_download.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/auto_download.tsx rename to x-pack/plugins/security_solution/public/common/components/auto_download/auto_download.tsx diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx index abbc168128831..47d7a0b407a32 100644 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { noop } from 'lodash/fp'; +import { noop, pick } from 'lodash/fp'; import React, { useCallback, useMemo } from 'react'; import { DropResult, DragDropContext } from 'react-beautiful-dnd'; import { useDispatch } from 'react-redux'; @@ -18,10 +18,13 @@ import { timelineSelectors } from '../../../timelines/store/timeline'; import { IdToDataProvider } from '../../store/drag_and_drop/model'; import { DataProvider } from '../../../timelines/components/timeline/data_providers/data_provider'; import { reArrangeProviders } from '../../../timelines/components/timeline/data_providers/helpers'; -import { ADDED_TO_TIMELINE_MESSAGE } from '../../hooks/translations'; +import { + ADDED_TO_TIMELINE_MESSAGE, + ADDED_TO_TIMELINE_TEMPLATE_MESSAGE, +} from '../../hooks/translations'; import { useAddToTimelineSensor } from '../../hooks/use_add_to_timeline'; import { displaySuccessToast, useStateToaster } from '../toasters'; -import { TimelineId } from '../../../../common/types/timeline'; +import { TimelineId, TimelineType } from '../../../../common/types/timeline'; import { addFieldToTimelineColumns, addProviderToTimeline, @@ -98,17 +101,27 @@ export const DragDropContextWrapperComponent: React.FC = ({ browserFields const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); const getDataProviders = useMemo(() => dragAndDropSelectors.getDataProvidersSelector(), []); - const activeTimelineDataProviders = useDeepEqualSelector( - (state) => (getTimeline(state, TimelineId.active) ?? timelineDefaults)?.dataProviders + const { + dataProviders: activeTimelineDataProviders, + timelineType, + } = useDeepEqualSelector((state) => + pick( + ['dataProviders', 'timelineType'], + getTimeline(state, TimelineId.active) ?? timelineDefaults + ) ); const dataProviders = useDeepEqualSelector(getDataProviders); const [, dispatchToaster] = useStateToaster(); const onAddedToTimeline = useCallback( (fieldOrValue: string) => { - displaySuccessToast(ADDED_TO_TIMELINE_MESSAGE(fieldOrValue), dispatchToaster); + const message = + timelineType === TimelineType.template + ? ADDED_TO_TIMELINE_TEMPLATE_MESSAGE(fieldOrValue) + : ADDED_TO_TIMELINE_MESSAGE(fieldOrValue); + displaySuccessToast(message, dispatchToaster); }, - [dispatchToaster] + [dispatchToaster, timelineType] ); const onDragEnd = useCallback( diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/__snapshots__/event_details.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/event_details/__snapshots__/event_details.test.tsx.snap index 973d067d9e379..e9b11d9bcdf71 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/__snapshots__/event_details.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/event_details/__snapshots__/event_details.test.tsx.snap @@ -577,6 +577,7 @@ exports[`EventDetails rendering should match snapshot 1`] = ` } eventId="Y-6TfmcB0WOhS6qyMv3s" timelineId="test" + timelineTabType="query" /> , "id": "table-view", @@ -1157,6 +1158,7 @@ exports[`EventDetails rendering should match snapshot 1`] = ` } eventId="Y-6TfmcB0WOhS6qyMv3s" timelineId="test" + timelineTabType="query" /> , "id": "table-view", diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx index 6bc3a4904e031..fa0684e5593bf 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx @@ -51,6 +51,14 @@ const HoverActionsContainer = styled(EuiPanel)` HoverActionsContainer.displayName = 'HoverActionsContainer'; +const FullWidthFlexGroup = styled(EuiFlexGroup)` + width: 100%; +`; + +const FullWidthFlexItem = styled(EuiFlexItem)` + width: 100%; +`; + export const getColumns = ({ browserFields, columnHeaders, @@ -159,10 +167,15 @@ export const getColumns = ({ sortable: true, truncateText: false, render: (values: ToStringArray | null | undefined, data: EventFieldsData) => ( - + {values != null && values.map((value, i) => ( - )}

    - + ))} - + ), }, { diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx index 4659006050781..9ab286b120dd3 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { waitFor } from '@testing-library/dom'; import { ReactWrapper, shallow } from 'enzyme'; import React from 'react'; @@ -16,7 +17,7 @@ import { mockBrowserFields } from '../../containers/source/mock'; import { useMountAppended } from '../../utils/use_mount_appended'; import { mockAlertDetailsData } from './__mocks__'; import { TimelineEventsDetailsItem } from '../../../../common/search_strategy'; -import { waitFor } from '@testing-library/dom'; +import { TimelineTabs } from '../../../../common/types/timeline'; jest.mock('../link_to'); describe('EventDetails', () => { @@ -27,6 +28,7 @@ describe('EventDetails', () => { id: mockDetailItemDataId, isAlert: false, onViewSelected: jest.fn(), + timelineTabType: TimelineTabs.query, timelineId: 'test', view: EventsViewType.summaryView, }; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx index 291893fe682b4..123a3fa7b610b 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx @@ -14,6 +14,7 @@ import { EventFieldsBrowser } from './event_fields_browser'; import { JsonView } from './json_view'; import * as i18n from './translations'; import { SummaryView } from './summary_view'; +import { TimelineTabs } from '../../../../common/types/timeline'; export type View = EventsViewType.tableView | EventsViewType.jsonView | EventsViewType.summaryView; export enum EventsViewType { @@ -29,6 +30,7 @@ interface Props { isAlert: boolean; view: EventsViewType; onViewSelected: (selected: EventsViewType) => void; + timelineTabType: TimelineTabs | 'flyout'; timelineId: string; } @@ -52,6 +54,7 @@ const EventDetailsComponent: React.FC = ({ id, view, onViewSelected, + timelineTabType, timelineId, isAlert, }) => { @@ -91,6 +94,7 @@ const EventDetailsComponent: React.FC = ({ data={data} eventId={id} timelineId={timelineId} + timelineTabType={timelineTabType} /> ), @@ -106,7 +110,7 @@ const EventDetailsComponent: React.FC = ({ ), }, ], - [alerts, browserFields, data, id, isAlert, timelineId] + [alerts, browserFields, data, id, isAlert, timelineId, timelineTabType] ); const selectedTab = useMemo(() => tabs.find((t) => t.id === view) ?? tabs[0], [tabs, view]); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.test.tsx index cd50eb7880e56..0fc29e7193d4d 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.test.tsx @@ -13,6 +13,7 @@ import { timelineActions } from '../../../timelines/store/timeline'; import { EventFieldsBrowser } from './event_fields_browser'; import { mockBrowserFields } from '../../containers/source/mock'; import { useMountAppended } from '../../utils/use_mount_appended'; +import { TimelineTabs } from '../../../../common/types/timeline'; jest.mock('@elastic/eui', () => { const original = jest.requireActual('@elastic/eui'); @@ -48,6 +49,7 @@ describe('EventFieldsBrowser', () => { data={mockDetailItemData} eventId={mockDetailItemDataId} timelineId="test" + timelineTabType={TimelineTabs.query} /> ); @@ -66,6 +68,7 @@ describe('EventFieldsBrowser', () => { data={mockDetailItemData} eventId={mockDetailItemDataId} timelineId="test" + timelineTabType={TimelineTabs.query} /> ); @@ -89,6 +92,7 @@ describe('EventFieldsBrowser', () => { data={mockDetailItemData} eventId={eventId} timelineId="test" + timelineTabType={TimelineTabs.query} /> ); @@ -108,6 +112,7 @@ describe('EventFieldsBrowser', () => { data={mockDetailItemData} eventId={eventId} timelineId="test" + timelineTabType={TimelineTabs.query} /> ); @@ -127,6 +132,7 @@ describe('EventFieldsBrowser', () => { data={mockDetailItemData} eventId={eventId} timelineId="test" + timelineTabType={TimelineTabs.query} /> ); @@ -158,6 +164,7 @@ describe('EventFieldsBrowser', () => { data={mockDetailItemData} eventId={mockDetailItemDataId} timelineId="test" + timelineTabType={TimelineTabs.query} /> ); @@ -182,6 +189,7 @@ describe('EventFieldsBrowser', () => { data={mockDetailItemData} eventId={mockDetailItemDataId} timelineId="test" + timelineTabType={TimelineTabs.query} /> ); @@ -196,6 +204,7 @@ describe('EventFieldsBrowser', () => { data={mockDetailItemData} eventId={mockDetailItemDataId} timelineId="test" + timelineTabType={TimelineTabs.query} /> ); @@ -220,6 +229,7 @@ describe('EventFieldsBrowser', () => { data={mockDetailItemData} eventId={mockDetailItemDataId} timelineId="test" + timelineTabType={TimelineTabs.query} /> ); @@ -238,6 +248,7 @@ describe('EventFieldsBrowser', () => { data={mockDetailItemData} eventId={mockDetailItemDataId} timelineId="test" + timelineTabType={TimelineTabs.query} /> ); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx index cd1579b299093..9733fafbe1c4d 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx @@ -29,12 +29,14 @@ import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; import { getColumns } from './columns'; import { EVENT_FIELDS_TABLE_CLASS_NAME, onEventDetailsTabKeyPressed, search } from './helpers'; import { useDeepEqualSelector } from '../../hooks/use_selector'; +import { TimelineTabs } from '../../../../common/types/timeline'; interface Props { browserFields: BrowserFields; data: TimelineEventsDetailsItem[]; eventId: string; timelineId: string; + timelineTabType: TimelineTabs | 'flyout'; } const TableWrapper = styled.div` @@ -87,7 +89,7 @@ const getAriaRowindex = (timelineEventsDetailsItem: TimelineEventsDetailsItem) = /** Renders a table view or JSON view of the `ECS` `data` */ export const EventFieldsBrowser = React.memo( - ({ browserFields, data, eventId, timelineId }) => { + ({ browserFields, data, eventId, timelineTabType, timelineId }) => { const containerElement = useRef(null); const dispatch = useDispatch(); const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); @@ -156,7 +158,7 @@ export const EventFieldsBrowser = React.memo( columnHeaders, eventId, onUpdateColumns, - contextId: `event-fields-browser-for-${timelineId}`, + contextId: `event-fields-browser-for-${timelineId}-${timelineTabType}`, timelineId, toggleColumn, getLinkValue, @@ -167,6 +169,7 @@ export const EventFieldsBrowser = React.memo( eventId, onUpdateColumns, timelineId, + timelineTabType, toggleColumn, getLinkValue, ] diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/event_details_flyout.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/event_details_flyout.tsx index 48bdebbc0aa4f..9c09f2e696104 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/event_details_flyout.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/event_details_flyout.tsx @@ -39,7 +39,7 @@ const EventDetailsFlyoutComponent: React.FC = ({ const dispatch = useDispatch(); const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); const expandedEvent = useDeepEqualSelector( - (state) => (getTimeline(state, timelineId) ?? timelineDefaults)?.expandedEvent ?? {} + (state) => (getTimeline(state, timelineId) ?? timelineDefaults)?.expandedEvent?.query ?? {} ); const handleClearSelection = useCallback(() => { @@ -75,6 +75,7 @@ const EventDetailsFlyoutComponent: React.FC = ({ isAlert={isAlert} loading={loading} timelineId={timelineId} + timelineTabType="flyout" /> diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx index 423b3566e4eb5..6250345579cff 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx @@ -5,12 +5,13 @@ */ import React from 'react'; +import { waitFor, act } from '@testing-library/react'; import useResizeObserver from 'use-resize-observer/polyfilled'; import '../../mock/match_media'; import { mockIndexNames, mockIndexPattern, TestProviders } from '../../mock'; -import { mockEventViewerResponse } from './mock'; +import { mockEventViewerResponse, mockEventViewerResponseWithEvents } from './mock'; import { StatefulEventsViewer } from '.'; import { EventsViewer } from './events_viewer'; import { defaultHeaders } from './default_headers'; @@ -30,6 +31,15 @@ jest.mock('../../../timelines/components/graph_overlay', () => ({ GraphOverlay: jest.fn(() =>
    ), })); +const mockDispatch = jest.fn(); +jest.mock('react-redux', () => { + const original = jest.requireActual('react-redux'); + return { + ...original, + useDispatch: () => mockDispatch, + }; +}); + jest.mock('@elastic/eui', () => { const original = jest.requireActual('@elastic/eui'); return { @@ -50,6 +60,9 @@ const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock; jest.mock('use-resize-observer/polyfilled'); mockUseResizeObserver.mockImplementation(() => ({})); +const mockUseTimelineEvents: jest.Mock = useTimelineEvents as jest.Mock; +jest.mock('../../../timelines/containers'); + const from = '2019-08-26T22:10:56.791Z'; const to = '2019-08-27T22:10:56.794Z'; @@ -108,14 +121,51 @@ describe('EventsViewer', () => { start: from, scopeId: SourcererScopeName.timeline, }; - beforeEach(() => { - (useTimelineEvents as jest.Mock).mockReturnValue([false, mockEventViewerResponse]); + mockUseTimelineEvents.mockReset(); }); beforeAll(() => { mockUseSourcererScope.mockImplementation(() => defaultMocks); }); + + describe('event details', () => { + beforeEach(() => { + mockUseTimelineEvents.mockReturnValue([false, mockEventViewerResponseWithEvents]); + }); + + test('call the right reduce action to show event details', async () => { + const wrapper = mount( + + + + ); + + await act(async () => { + wrapper.find(`[data-test-subj="expand-event"]`).first().simulate('click'); + }); + + await waitFor(() => { + expect(mockDispatch).toBeCalledTimes(2); + expect(mockDispatch.mock.calls[1][0]).toEqual({ + payload: { + event: { + eventId: 'yb8TkHYBRgU82_bJu_rY', + indexName: 'auditbeat-7.10.1-2020.12.18-000001', + }, + tabType: 'query', + timelineId: 'test-stateful-events-viewer', + }, + type: 'x-pack/security_solution/local/timeline/TOGGLE_EXPANDED_EVENT', + }); + }); + }); + }); + describe('rendering', () => { + beforeEach(() => { + mockUseTimelineEvents.mockReturnValue([false, mockEventViewerResponse]); + }); + test('it renders the "Showing..." subtitle with the expected event count', () => { const wrapper = mount( @@ -160,57 +210,66 @@ describe('EventsViewer', () => { ); }); }); - describe('loading', () => { - beforeAll(() => { - mockUseSourcererScope.mockImplementation(() => ({ ...defaultMocks, loading: true })); - }); - test('it does NOT render fetch index pattern is loading', () => { - const wrapper = mount( - - - - ); + }); - expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).first().exists()).toBe( - false - ); - }); + describe('loading', () => { + beforeAll(() => { + mockUseSourcererScope.mockImplementation(() => ({ ...defaultMocks, loading: true })); + }); + beforeEach(() => { + mockUseTimelineEvents.mockReturnValue([false, mockEventViewerResponse]); + }); - test('it does NOT render when start is empty', () => { - testProps = { - ...testProps, - start: '', - }; - const wrapper = mount( - - - - ); + test('it does NOT render fetch index pattern is loading', () => { + const wrapper = mount( + + + + ); - expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).first().exists()).toBe( - false - ); - }); + expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).first().exists()).toBe( + false + ); + }); - test('it does NOT render when end is empty', () => { - testProps = { - ...testProps, - end: '', - }; - const wrapper = mount( - - - - ); + test('it does NOT render when start is empty', () => { + testProps = { + ...testProps, + start: '', + }; + const wrapper = mount( + + + + ); - expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).first().exists()).toBe( - false - ); - }); + expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).first().exists()).toBe( + false + ); + }); + + test('it does NOT render when end is empty', () => { + testProps = { + ...testProps, + end: '', + }; + const wrapper = mount( + + + + ); + + expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).first().exists()).toBe( + false + ); }); }); describe('headerFilterGroup', () => { + beforeEach(() => { + mockUseTimelineEvents.mockReturnValue([false, mockEventViewerResponse]); + }); + test('it renders the provided headerFilterGroup', () => { const wrapper = mount( @@ -284,6 +343,10 @@ describe('EventsViewer', () => { }); describe('utilityBar', () => { + beforeEach(() => { + mockUseTimelineEvents.mockReturnValue([false, mockEventViewerResponse]); + }); + test('it renders the provided utilityBar when Resolver is NOT showing, because graphEventId is undefined', () => { const wrapper = mount( @@ -313,6 +376,10 @@ describe('EventsViewer', () => { }); describe('header inspect button', () => { + beforeEach(() => { + mockUseTimelineEvents.mockReturnValue([false, mockEventViewerResponse]); + }); + test('it renders the inspect button when Resolver is NOT showing, because graphEventId is undefined', () => { const wrapper = mount( diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx index 7d38e3b732fc0..1d06f07bc774b 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx @@ -6,21 +6,15 @@ import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; import { isEmpty } from 'lodash/fp'; -import React, { useEffect, useMemo, useState, useRef } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import styled from 'styled-components'; import deepEqual from 'fast-deep-equal'; -import { useDispatch } from 'react-redux'; import { Direction } from '../../../../common/search_strategy'; import { BrowserFields, DocValueFields } from '../../containers/source'; import { useTimelineEvents } from '../../../timelines/containers'; -import { timelineActions } from '../../../timelines/store/timeline'; import { useKibana } from '../../lib/kibana'; -import { - ColumnHeaderOptions, - KqlMode, - TimelineTabs, -} from '../../../timelines/store/timeline/model'; +import { ColumnHeaderOptions, KqlMode } from '../../../timelines/store/timeline/model'; import { HeaderSection } from '../header_section'; import { defaultHeaders } from '../../../timelines/components/timeline/body/column_headers/default_headers'; import { Sort } from '../../../timelines/components/timeline/body/sort'; @@ -45,7 +39,11 @@ import { inputsModel } from '../../store'; import { useManageTimeline } from '../../../timelines/components/manage_timeline'; import { ExitFullScreen } from '../exit_full_screen'; import { useGlobalFullScreen } from '../../containers/use_full_screen'; -import { TimelineExpandedEvent, TimelineId } from '../../../../common/types/timeline'; +import { + TimelineExpandedEventType, + TimelineId, + TimelineTabs, +} from '../../../../common/types/timeline'; import { GraphOverlay } from '../../../timelines/components/graph_overlay'; import { SELECTOR_TIMELINE_GLOBAL_CONTAINER } from '../../../timelines/components/timeline/styles'; @@ -114,7 +112,7 @@ interface Props { deletedEventIds: Readonly; docValueFields: DocValueFields[]; end: string; - expandedEvent: TimelineExpandedEvent; + expandedEvent: TimelineExpandedEventType; filters: Filter[]; headerFilterGroup?: React.ReactNode; height?: number; @@ -160,7 +158,6 @@ const EventsViewerComponent: React.FC = ({ utilityBar, graphEventId, }) => { - const dispatch = useDispatch(); const { globalFullScreen } = useGlobalFullScreen(); const columnsHeader = isEmpty(columns) ? defaultHeaders : columns; const kibana = useKibana(); @@ -191,9 +188,6 @@ const EventsViewerComponent: React.FC = ({ [justTitle] ); - const prevCombinedQueries = useRef<{ - filterQuery: string; - } | null>(null); const combinedQueries = combineQueries({ config: esQuery.getEsQueryConfig(kibana.services.uiSettings), dataProviders, @@ -220,12 +214,6 @@ const EventsViewerComponent: React.FC = ({ queryFields, ]); - const prevSortField = useRef< - Array<{ - field: string; - direction: Direction; - }> - >([]); const sortField = useMemo( () => sort.map(({ columnId, sortDirection }) => ({ @@ -251,17 +239,6 @@ const EventsViewerComponent: React.FC = ({ skip: !canQueryTimeline, }); - useEffect(() => { - if (!deepEqual(prevCombinedQueries.current, combinedQueries)) { - prevCombinedQueries.current = combinedQueries; - dispatch(timelineActions.toggleExpandedEvent({ timelineId: id })); - } - if (!deepEqual(prevSortField.current, sortField)) { - prevSortField.current = sortField; - dispatch(timelineActions.toggleExpandedEvent({ timelineId: id })); - } - }, [combinedQueries, dispatch, id, sortField]); - const totalCountMinusDeleted = useMemo( () => (totalCount > 0 ? totalCount - deletedEventIds.length : 0), [deletedEventIds.length, totalCount] diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx index 3272b0306f9c9..d7310ea776659 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx @@ -166,7 +166,7 @@ const makeMapStateToProps = () => { columns, dataProviders, deletedEventIds, - expandedEvent, + expandedEvent: expandedEvent?.query ?? {}, excludedRowRendererIds, filters: getGlobalFiltersQuerySelector(state), id, diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/mock.ts b/x-pack/plugins/security_solution/public/common/components/events_viewer/mock.ts index d2bd940dcc266..153992d9f1adb 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/mock.ts +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/mock.ts @@ -12,3 +12,91 @@ export const mockEventViewerResponse = { }, events: [], }; + +export const mockEventViewerResponseWithEvents = { + totalCount: 1, + pageInfo: { + activePage: 0, + fakeTotalCount: 100, + }, + events: [ + { + ecs: { + _id: 'yb8TkHYBRgU82_bJu_rY', + timestamp: '2020-12-23T14:49:39.957Z', + _index: 'auditbeat-7.10.1-2020.12.18-000001', + '@timestamp': ['2020-12-23T14:49:39.957Z'], + event: { + module: ['system'], + action: ['process_started'], + category: ['process'], + dataset: ['process'], + kind: ['event'], + type: ['start'], + }, + host: { + name: ['handsome'], + os: { + family: ['darwin'], + }, + id: ['33'], + ip: ['0.0.0.0'], + }, + user: { + name: ['handsome'], + }, + message: ['Process node (PID: 77895) by user handsome STARTED'], + agent: { + type: ['auditbeat'], + }, + process: { + hash: { + sha1: ['`12345678987654323456Y7U87654`'], + }, + pid: ['77895'], + name: ['node'], + ppid: ['73537'], + args: [ + '/Users/handsome/.nvm/versions/node/v14.15.3/bin/node', + '/Users/handsome/Documents/workspace/kibana/node_modules/jest-worker/build/workers/processChild.js', + ], + entity_id: ['3arNfOyR9NwR2u03'], + executable: ['/Users/handsome/.nvm/versions/node/v14.15.3/bin/node'], + working_directory: ['/Users/handsome/Documents/workspace/kibana/x-pack'], + }, + }, + data: [ + { + field: '@timestamp', + value: ['2020-12-23T14:49:39.957Z'], + }, + { + field: 'event.module', + value: ['system'], + }, + { + field: 'event.action', + value: ['process_started'], + }, + { + field: 'host.name', + value: ['handsome'], + }, + { + field: 'user.name', + value: ['handsome'], + }, + { + field: 'message', + value: ['Process node (PID: 77895) by user handsome STARTED'], + }, + { + field: 'event.dataset', + value: ['process'], + }, + ], + _id: 'yb8TkHYBRgU82_bJu_rY', + _index: 'auditbeat-7.10.1-2020.12.18-000001', + }, + ], +}; diff --git a/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/timeline/plugin.tsx b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/timeline/plugin.tsx index 8d2488b269d76..4b03e7e0d32c4 100644 --- a/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/timeline/plugin.tsx +++ b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/timeline/plugin.tsx @@ -8,6 +8,7 @@ import React, { useCallback, memo } from 'react'; import { EuiSelectableOption, EuiModalBody, + EuiModalHeader, EuiMarkdownEditorUiPlugin, EuiCodeBlock, } from '@elastic/eui'; @@ -47,24 +48,32 @@ const TimelineEditorComponent: React.FC = ({ onClosePopover [] ); + const handleTimelineChange = useCallback( + (timelineTitle, timelineId, graphEventId) => { + const url = formatUrl(getTimelineUrl(timelineId ?? '', graphEventId), { + absolute: true, + skipSearch: true, + }); + onInsert(`[${timelineTitle}](${url})`, { + block: false, + }); + }, + [formatUrl, onInsert] + ); + return ( - - { - const url = formatUrl(getTimelineUrl(timelineId ?? '', graphEventId), { - absolute: true, - skipSearch: true, - }); - onInsert(`[${timelineTitle}](${url})`, { - block: false, - }); - }} - onClosePopover={onClosePopover} - timelineType={TimelineType.default} - /> - + <> + + + + + ); }; diff --git a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx index 5f567508a4011..e71cf054c87b4 100644 --- a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx @@ -58,7 +58,7 @@ const HeaderChildrenFlexItem = styled(EuiFlexItem)` const HistogramPanel = styled(Panel)<{ height?: number }>` display: flex; flex-direction: column; - ${({ height }) => (height != null ? `height: ${height}px;` : '')} + ${({ height }) => (height != null ? `min-height: ${height}px;` : '')} `; export const MatrixHistogramComponent: React.FC = ({ diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts index 36cdc807c4c0c..891e7bfffe868 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts @@ -11,7 +11,7 @@ import { HostsTableType } from '../../../../hosts/store/model'; import { RouteSpyState, SiemRouteType } from '../../../utils/route/types'; import { TabNavigationProps } from '../tab_navigation/types'; import { NetworkRouteType } from '../../../../network/pages/navigation/types'; -import { TimelineTabs } from '../../../../timelines/store/timeline/model'; +import { TimelineTabs } from '../../../../../common/types/timeline'; const setBreadcrumbsMock = jest.fn(); const chromeMock = { diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/index.test.tsx index 158da3be3bbf7..f2fbe48c97c83 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/index.test.tsx @@ -14,7 +14,7 @@ import { navTabs } from '../../../app/home/home_navigations'; import { HostsTableType } from '../../../hosts/store/model'; import { RouteSpyState } from '../../utils/route/types'; import { SiemNavigationProps, SiemNavigationComponentProps } from './types'; -import { TimelineTabs } from '../../../timelines/store/timeline/model'; +import { TimelineTabs } from '../../../../common/types/timeline'; jest.mock('react-router-dom', () => { const original = jest.requireActual('react-router-dom'); diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx index f4ffc25146be5..e5c011cdc14be 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx @@ -6,12 +6,12 @@ import { mount } from 'enzyme'; import React from 'react'; +import { TimelineTabs } from '../../../../../common/types/timeline'; import { navTabs } from '../../../../app/home/home_navigations'; import { SecurityPageName } from '../../../../app/types'; import { navTabsHostDetails } from '../../../../hosts/pages/details/nav_tabs'; import { HostsTableType } from '../../../../hosts/store/model'; -import { TimelineTabs } from '../../../../timelines/store/timeline/model'; import { RouteSpyState } from '../../../utils/route/types'; import { CONSTANTS } from '../../url_state/constants'; import { TabNavigationComponent } from './'; diff --git a/x-pack/plugins/security_solution/public/common/components/paginated_table/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/paginated_table/__snapshots__/index.test.tsx.snap index a6aa844919709..a2a9c30ca4e1c 100644 --- a/x-pack/plugins/security_solution/public/common/components/paginated_table/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/paginated_table/__snapshots__/index.test.tsx.snap @@ -460,12 +460,7 @@ exports[`Paginated Table Component rendering it renders the default load more ta "euiRangeTrackHeight": "2px", "euiRangeTrackRadius": "4px", "euiRangeTrackWidth": "100%", - "euiResizableButtonSizeModifiers": Object { - "sizeExtraLarge": "40px", - "sizeLarge": "24px", - "sizeMedium": "16px", - "sizeSmall": "12px", - }, + "euiResizableButtonSize": "16px", "euiResizableButtonTransitionSpeed": "150ms", "euiScrollBar": "16px", "euiScrollBarCorner": "6px", diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts b/x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts index 9932e52b6a1d1..9f51ecf9483b2 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts +++ b/x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts @@ -12,11 +12,11 @@ import * as H from 'history'; import { Query, Filter } from '../../../../../../../src/plugins/data/public'; import { url } from '../../../../../../../src/plugins/kibana_utils/public'; -import { TimelineId } from '../../../../common/types/timeline'; +import { TimelineId, TimelineTabs } from '../../../../common/types/timeline'; import { SecurityPageName } from '../../../app/types'; import { inputsSelectors, State } from '../../store'; import { UrlInputsModel } from '../../store/inputs/model'; -import { TimelineTabs, TimelineUrl } from '../../../timelines/store/timeline/model'; +import { TimelineUrl } from '../../../timelines/store/timeline/model'; import { timelineSelectors } from '../../../timelines/store/timeline'; import { formatDate } from '../super_date_picker'; import { NavTab } from '../navigation/types'; diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/test_dependencies.ts b/x-pack/plugins/security_solution/public/common/components/url_state/test_dependencies.ts index bf5b6b1719605..d835636aa2778 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/test_dependencies.ts +++ b/x-pack/plugins/security_solution/public/common/components/url_state/test_dependencies.ts @@ -17,7 +17,7 @@ import { Query } from '../../../../../../../src/plugins/data/public'; import { networkModel } from '../../../network/store'; import { hostsModel } from '../../../hosts/store'; import { HostsTableType } from '../../../hosts/store/model'; -import { TimelineTabs } from '../../../timelines/store/timeline/model'; +import { TimelineTabs } from '../../../../common/types/timeline'; type Action = 'PUSH' | 'POP' | 'REPLACE'; const pop: Action = 'POP'; diff --git a/x-pack/plugins/security_solution/public/common/hooks/translations.ts b/x-pack/plugins/security_solution/public/common/hooks/translations.ts index 2c6300046b7bd..0f982854865e4 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/translations.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/translations.ts @@ -12,6 +12,12 @@ export const ADDED_TO_TIMELINE_MESSAGE = (fieldOrValue: string) => defaultMessage: `Added {fieldOrValue} to timeline`, }); +export const ADDED_TO_TIMELINE_TEMPLATE_MESSAGE = (fieldOrValue: string) => + i18n.translate('xpack.securitySolution.hooks.useAddToTimeline.template.addedFieldMessage', { + values: { fieldOrValue }, + defaultMessage: `Added {fieldOrValue} to timeline template`, + }); + export const STATUS_CODE = i18n.translate( 'xpack.securitySolution.components.ml.api.errors.statusCodeFailureTitle', { diff --git a/x-pack/plugins/security_solution/public/common/mock/global_state.ts b/x-pack/plugins/security_solution/public/common/mock/global_state.ts index db21847991534..320c3a0736540 100644 --- a/x-pack/plugins/security_solution/public/common/mock/global_state.ts +++ b/x-pack/plugins/security_solution/public/common/mock/global_state.ts @@ -24,13 +24,12 @@ import { DEFAULT_INDEX_PATTERN, } from '../../../common/constants'; import { networkModel } from '../../network/store'; -import { TimelineType, TimelineStatus } from '../../../common/types/timeline'; +import { TimelineType, TimelineStatus, TimelineTabs } from '../../../common/types/timeline'; import { mockManagementState } from '../../management/store/reducer'; import { ManagementState } from '../../management/types'; import { initialSourcererState, SourcererScopeName } from '../store/sourcerer/model'; import { mockBrowserFields, mockDocValueFields } from '../containers/source/mock'; import { mockIndexPattern } from './index_pattern'; -import { TimelineTabs } from '../../timelines/store/timeline/model'; export const mockGlobalState: State = { app: { diff --git a/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts b/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts index c8d9fc981d880..03109803eb9d8 100644 --- a/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts +++ b/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts @@ -5,14 +5,19 @@ */ import { FilterStateStore } from '../../../../../../src/plugins/data/common/es_query/filters/meta_filter'; -import { TimelineId, TimelineType, TimelineStatus } from '../../../common/types/timeline'; +import { + TimelineId, + TimelineType, + TimelineStatus, + TimelineTabs, +} from '../../../common/types/timeline'; import { OpenTimelineResult } from '../../timelines/components/open_timeline/types'; import { GetAllTimeline, SortFieldTimeline, TimelineResult, Direction } from '../../graphql/types'; import { TimelineEventsDetailsItem } from '../../../common/search_strategy'; import { allTimelinesQuery } from '../../timelines/containers/all/index.gql_query'; import { CreateTimelineProps } from '../../detections/components/alerts_table/types'; -import { TimelineModel, TimelineTabs } from '../../timelines/store/timeline/model'; +import { TimelineModel } from '../../timelines/store/timeline/model'; import { timelineDefaults } from '../../timelines/store/timeline/defaults'; export interface MockedProvidedQuery { diff --git a/x-pack/plugins/security_solution/public/common/store/actions.ts b/x-pack/plugins/security_solution/public/common/store/actions.ts index f4134b5c47c2c..dc3125d5db25b 100644 --- a/x-pack/plugins/security_solution/public/common/store/actions.ts +++ b/x-pack/plugins/security_solution/public/common/store/actions.ts @@ -5,7 +5,6 @@ */ import { EndpointAction } from '../../management/pages/endpoint_hosts/store/action'; -import { PolicyListAction } from '../../management/pages/policy/store/policy_list'; import { PolicyDetailsAction } from '../../management/pages/policy/store/policy_details'; import { TrustedAppsPageAction } from '../../management/pages/trusted_apps/store/action'; @@ -18,6 +17,5 @@ import { RoutingAction } from './routing'; export type AppAction = | EndpointAction | RoutingAction - | PolicyListAction | PolicyDetailsAction | TrustedAppsPageAction; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx index d251cce381536..64e916f87b09d 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx @@ -19,10 +19,14 @@ import { } from '../../../common/mock/'; import { CreateTimeline, UpdateTimelineLoading } from './types'; import { Ecs } from '../../../../common/ecs'; -import { TimelineId, TimelineType, TimelineStatus } from '../../../../common/types/timeline'; +import { + TimelineId, + TimelineType, + TimelineStatus, + TimelineTabs, +} from '../../../../common/types/timeline'; import { ISearchStart } from '../../../../../../../src/plugins/data/public'; import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks'; -import { TimelineTabs } from '../../../timelines/store/timeline/model'; jest.mock('apollo-client'); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/utils.test.ts b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/utils.test.ts index a17aa133ff438..048a7dce807ef 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/utils.test.ts +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/utils.test.ts @@ -51,15 +51,12 @@ describe('stepRuleActions utils', () => { const actionTypeRegistry = actionTypeRegistryMock.create(); beforeAll(() => { - const actionMock = { + const actionMock = actionTypeRegistryMock.createMockActionTypeModel({ id: 'id', iconClass: 'iconClass', validateParams: validateParamsMock, selectMessage: 'message', - validateConnector: jest.fn(), - actionConnectorFields: null, - actionParamsFields: null, - }; + }); actionTypeRegistry.get.mockReturnValue(actionMock); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/modal.tsx b/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/modal.tsx index f0e47fcd5c104..57c4eee95cd8c 100644 --- a/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/modal.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/modal.tsx @@ -32,8 +32,8 @@ import { useAppToasts } from '../../../common/hooks/use_app_toasts'; import * as i18n from './translations'; import { buildColumns } from './table_helpers'; import { ValueListsForm } from './form'; -import { AutoDownload } from './auto_download'; import { ReferenceErrorModal } from './reference_error_modal'; +import { AutoDownload } from '../../../common/components/auto_download/auto_download'; interface ValueListsModalProps { onClose: () => void; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/columns.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/columns.tsx index 57b86119dc164..79cfd53a4fa00 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/columns.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/columns.tsx @@ -9,6 +9,7 @@ import React from 'react'; import { EuiButtonIcon, EuiBasicTableColumn, EuiToolTip } from '@elastic/eui'; import { History } from 'history'; +import { NamespaceType } from '../../../../../../../../lists/common'; import { FormatUrl } from '../../../../../../common/components/link_to'; import { LinkAnchor } from '../../../../../../common/components/links'; import * as i18n from './translations'; @@ -16,7 +17,11 @@ import { ExceptionListInfo } from './use_all_exception_lists'; import { getRuleDetailsUrl } from '../../../../../../common/components/link_to/redirect_to_detection_engine'; export type AllExceptionListsColumns = EuiBasicTableColumn; -export type Func = (listId: string) => () => void; +export type Func = (arg: { + id: string; + listId: string; + namespaceType: NamespaceType; +}) => () => void; export const getAllExceptionListsColumns = ( onExport: Func, @@ -96,9 +101,13 @@ export const getAllExceptionListsColumns = ( align: 'center', isExpander: false, width: '25px', - render: (list: ExceptionListInfo) => ( + render: ({ id, list_id: listId, namespace_type: namespaceType }: ExceptionListInfo) => ( @@ -108,10 +117,14 @@ export const getAllExceptionListsColumns = ( align: 'center', width: '25px', isExpander: false, - render: (list: ExceptionListInfo) => ( + render: ({ id, list_id: listId, namespace_type: namespaceType }: ExceptionListInfo) => ( diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table.tsx index 65aaaea06b40f..ac9c558022c26 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useMemo, useEffect, useCallback, useState, ChangeEvent } from 'react'; +import React, { useMemo, useEffect, useCallback, useState } from 'react'; import { EuiBasicTable, EuiEmptyPrompt, @@ -16,8 +16,10 @@ import styled from 'styled-components'; import { History } from 'history'; import { set } from 'lodash/fp'; +import { AutoDownload } from '../../../../../../common/components/auto_download/auto_download'; +import { NamespaceType } from '../../../../../../../../lists/common'; import { useKibana } from '../../../../../../common/lib/kibana'; -import { useExceptionLists } from '../../../../../../shared_imports'; +import { useApi, useExceptionLists } from '../../../../../../shared_imports'; import { FormatUrl } from '../../../../../../common/components/link_to'; import { HeaderSection } from '../../../../../../common/components/header_section'; import { Loader } from '../../../../../../common/components/loader'; @@ -51,6 +53,7 @@ export const ExceptionListsTable = React.memo( const { services: { http, notifications }, } = useKibana(); + const { exportExceptionList } = useApi(http); const [filters, setFilters] = useState({ name: null, list_id: null, @@ -69,10 +72,67 @@ export const ExceptionListsTable = React.memo( }); const [initLoading, setInitLoading] = useState(true); const [lastUpdated, setLastUpdated] = useState(Date.now()); + const [deletingListIds, setDeletingListIds] = useState([]); + const [exportingListIds, setExportingListIds] = useState([]); + const [exportDownload, setExportDownload] = useState<{ name?: string; blob?: Blob }>({}); - const handleDelete = useCallback((id: string) => () => {}, []); + const handleDelete = useCallback( + ({ + id, + listId, + namespaceType, + }: { + id: string; + listId: string; + namespaceType: NamespaceType; + }) => async () => { + try { + setDeletingListIds((ids) => [...ids, id]); + // route to patch rules with associated exception list + } catch (error) { + notifications.toasts.addError(error, { title: i18n.EXCEPTION_DELETE_ERROR }); + } finally { + setDeletingListIds((ids) => [...ids.filter((_id) => _id !== id)]); + } + }, + [notifications.toasts] + ); - const handleExport = useCallback((id: string) => () => {}, []); + const handleExportSuccess = useCallback( + (listId: string) => (blob: Blob): void => { + setExportDownload({ name: listId, blob }); + }, + [] + ); + + const handleExportError = useCallback( + (err: Error) => { + notifications.toasts.addError(err, { title: i18n.EXCEPTION_EXPORT_ERROR }); + }, + [notifications.toasts] + ); + + const handleExport = useCallback( + ({ + id, + listId, + namespaceType, + }: { + id: string; + listId: string; + namespaceType: NamespaceType; + }) => async () => { + setExportingListIds((ids) => [...ids, id]); + await exportExceptionList({ + id, + listId, + namespaceType, + onError: handleExportError, + onSuccess: handleExportSuccess(listId), + }); + }, + [exportExceptionList, handleExportError, handleExportSuccess] + ); const exceptionsColumns = useMemo((): AllExceptionListsColumns[] => { return getAllExceptionListsColumns(handleExport, handleDelete, history, formatUrl); @@ -122,14 +182,6 @@ export const ExceptionListsTable = React.memo( setFilters(formattedFilter); }, []); - const handleSearchChange = useCallback( - (event: ChangeEvent) => { - const val = event.target.value; - handleSearch(val); - }, - [handleSearch] - ); - const paginationMemo = useMemo( () => ({ pageIndex: pagination.page - 1, @@ -140,8 +192,23 @@ export const ExceptionListsTable = React.memo( [pagination] ); + const handleOnDownload = useCallback(() => { + setExportDownload({}); + }, []); + + const tableItems = (data ?? []).map((item) => ({ + ...item, + isDeleting: deletingListIds.includes(item.id), + isExporting: exportingListIds.includes(item.id), + })); + return ( <> + <> {loadingTableInfo && ( @@ -162,7 +229,6 @@ export const ExceptionListsTable = React.memo( aria-label={i18n.EXCEPTIONS_LISTS_SEARCH_PLACEHOLDER} placeholder={i18n.EXCEPTIONS_LISTS_SEARCH_PLACEHOLDER} onSearch={handleSearch} - onChange={handleSearchChange} disabled={initLoading} incremental={false} fullWidth @@ -188,7 +254,7 @@ export const ExceptionListsTable = React.memo( columns={exceptionsColumns} isSelectable={!hasNoPermissions ?? false} itemId="id" - items={data ?? []} + items={tableItems} noItemsMessage={emptyPrompt} onChange={() => {}} pagination={paginationMemo} diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/translations.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/translations.ts index 2eba8fb2e579b..7483b8e943d30 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/translations.ts @@ -35,7 +35,7 @@ export const LIST_DATE_CREATED_TITLE = i18n.translate( ); export const LIST_DATE_UPDATED_TITLE = i18n.translate( - 'xpack.securitySolution.detectionEngine.rules.all.exceptions.dateUPdatedTitle', + 'xpack.securitySolution.detectionEngine.rules.all.exceptions.dateUpdatedTitle', { defaultMessage: 'Last edited', } @@ -75,3 +75,24 @@ export const NO_LISTS_BODY = i18n.translate( defaultMessage: "We weren't able to find any exception lists.", } ); + +export const EXCEPTION_EXPORT_SUCCESS = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.all.exceptions.exportSuccess', + { + defaultMessage: 'Exception list export success', + } +); + +export const EXCEPTION_EXPORT_ERROR = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.all.exceptions.exportError', + { + defaultMessage: 'Exception list export error', + } +); + +export const EXCEPTION_DELETE_ERROR = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.all.exceptions.deleteError', + { + defaultMessage: 'Error occurred deleting exception list', + } +); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/use_all_exception_lists.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/use_all_exception_lists.tsx index 4b47080cc2da1..3f343da605213 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/use_all_exception_lists.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/use_all_exception_lists.tsx @@ -61,7 +61,7 @@ export const useAllExceptionLists = ({ const { data: rules } = await fetchRules({ pagination: { page: 1, - perPage: 500, + perPage: 10000, total: 0, }, signal: abortCtrl.signal, diff --git a/x-pack/plugins/security_solution/public/management/common/constants.ts b/x-pack/plugins/security_solution/public/management/common/constants.ts index cd4ce743bb701..66b6fefac607c 100644 --- a/x-pack/plugins/security_solution/public/management/common/constants.ts +++ b/x-pack/plugins/security_solution/public/management/common/constants.ts @@ -18,8 +18,6 @@ export const MANAGEMENT_ROUTING_TRUSTED_APPS_PATH = `${MANAGEMENT_ROUTING_ROOT_P // --[ STORE ]--------------------------------------------------------------------------- /** The SIEM global store namespace where the management state will be mounted */ export const MANAGEMENT_STORE_GLOBAL_NAMESPACE: ManagementStoreGlobalNamespace = 'management'; -/** Namespace within the Management state where policy list state is maintained */ -export const MANAGEMENT_STORE_POLICY_LIST_NAMESPACE = 'policyList'; /** Namespace within the Management state where policy details state is maintained */ export const MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE = 'policyDetails'; /** Namespace within the Management state where endpoint-host state is maintained */ diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/endpoint_pagination.test.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/endpoint_pagination.test.ts index 1e3a92e6ec135..789e8a1d1d5f4 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/endpoint_pagination.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/endpoint_pagination.test.ts @@ -26,7 +26,7 @@ import { } from '../../../../common/store/test_utils'; import { getEndpointListPath } from '../../../common/routing'; -jest.mock('../../policy/store/policy_list/services/ingest', () => ({ +jest.mock('../../policy/store/services/ingest', () => ({ sendGetAgentPolicyList: () => Promise.resolve({ items: [] }), sendGetEndpointSecurityPackage: () => Promise.resolve({}), })); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts index d19b3a0ce4177..8c1841aace273 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts @@ -23,7 +23,7 @@ import { endpointListReducer } from './reducer'; import { endpointMiddlewareFactory } from './middleware'; import { getEndpointListPath } from '../../../common/routing'; -jest.mock('../../policy/store/policy_list/services/ingest', () => ({ +jest.mock('../../policy/store/services/ingest', () => ({ sendGetAgentConfigList: () => Promise.resolve({ items: [] }), sendGetAgentPolicyList: () => Promise.resolve({ items: [] }), sendGetEndpointSecurityPackage: () => Promise.resolve({}), diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts index a78bd5fde3216..3a51846de096f 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts @@ -25,7 +25,7 @@ import { sendGetEndpointSecurityPackage, sendGetAgentPolicyList, sendGetFleetAgentsWithEndpoint, -} from '../../policy/store/policy_list/services/ingest'; +} from '../../policy/store/services/ingest'; import { AGENT_POLICY_SAVED_OBJECT_TYPE } from '../../../../../../fleet/common'; import { metadataCurrentIndexPattern } from '../../../../../common/endpoint/constants'; import { IIndexPattern, Query } from '../../../../../../../../src/plugins/data/public'; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/mock_endpoint_result_list.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/mock_endpoint_result_list.ts index 5b14b7d658965..7c7337933483a 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/mock_endpoint_result_list.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/mock_endpoint_result_list.ts @@ -19,7 +19,7 @@ import { INGEST_API_EPM_PACKAGES, INGEST_API_PACKAGE_POLICIES, INGEST_API_FLEET_AGENTS, -} from '../../policy/store/policy_list/services/ingest'; +} from '../../policy/store/services/ingest'; import { GetAgentPoliciesResponse, GetAgentPoliciesResponseItem, diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/policy_response_friendly_names.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/policy_response_friendly_names.ts index 07ab38fd52776..312b83201045c 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/policy_response_friendly_names.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/policy_response_friendly_names.ts @@ -17,7 +17,7 @@ const policyResponses: Array<[string, string]> = [ 'configure_elasticsearch_connection', i18n.translate( 'xpack.securitySolution.endpoint.details.policyResponse.configure_elasticsearch_connection', - { defaultMessage: 'Configure Elastic Search Connection' } + { defaultMessage: 'Configure Elasticsearch Connection' } ), ], [ @@ -162,7 +162,7 @@ const policyResponses: Array<[string, string]> = [ 'read_elasticsearch_config', i18n.translate( 'xpack.securitySolution.endpoint.details.policyResponse.read_elasticsearch_config', - { defaultMessage: 'Read ElasticSearch Config' } + { defaultMessage: 'Read Elasticsearch Config' } ), ], [ diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx index 487f5ddab5504..6446fefff1559 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx @@ -25,7 +25,7 @@ import { } from '../../../../../common/endpoint/types'; import { EndpointDocGenerator } from '../../../../../common/endpoint/generate_data'; import { POLICY_STATUS_TO_HEALTH_COLOR, POLICY_STATUS_TO_TEXT } from './host_constants'; -import { mockPolicyResultList } from '../../policy/store/policy_list/test_mock_utils'; +import { mockPolicyResultList } from '../../policy/store/test_mock_utils'; // not sure why this can't be imported from '../../../../common/mock/formatted_relative'; // but sure enough it needs to be inline in this one file @@ -39,8 +39,8 @@ jest.mock('@kbn/i18n/react', () => { }; }); jest.mock('../../../../common/components/link_to'); -jest.mock('../../policy/store/policy_list/services/ingest', () => { - const originalModule = jest.requireActual('../../policy/store/policy_list/services/ingest'); +jest.mock('../../policy/store/services/ingest', () => { + const originalModule = jest.requireActual('../../policy/store/services/ingest'); return { ...originalModule, sendGetEndpointSecurityPackage: () => Promise.resolve({}), diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware.ts index 2f9f0d6723749..cc286b4c478d3 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware.ts @@ -18,7 +18,7 @@ import { sendGetPackagePolicy, sendGetFleetAgentStatusForPolicy, sendPutPackagePolicy, -} from '../policy_list/services/ingest'; +} from '../services/ingest'; import { NewPolicyData, PolicyData } from '../../../../../../common/endpoint/types'; import { ImmutableMiddlewareFactory } from '../../../../../common/store'; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/action.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/action.ts deleted file mode 100644 index cfd053948922b..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/action.ts +++ /dev/null @@ -1,70 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { PolicyData } from '../../../../../../common/endpoint/types'; -import { ServerApiError } from '../../../../../common/types'; -import { GetAgentStatusResponse, GetPackagesResponse } from '../../../../../../../fleet/common'; - -interface ServerReturnedPolicyListData { - type: 'serverReturnedPolicyListData'; - payload: { - policyItems: PolicyData[]; - total: number; - pageSize: number; - pageIndex: number; - }; -} - -interface ServerFailedToReturnPolicyListData { - type: 'serverFailedToReturnPolicyListData'; - payload: ServerApiError; -} - -interface UserClickedPolicyListDeleteButton { - type: 'userClickedPolicyListDeleteButton'; - payload: { policyId: string }; -} - -interface UserOpenedPolicyListDeleteModal { - type: 'userOpenedPolicyListDeleteModal'; - payload: { agentPolicyId: string }; -} - -interface ServerDeletedPolicyFailure { - type: 'serverDeletedPolicyFailure'; - payload: ServerApiError; -} - -interface ServerDeletedPolicy { - type: 'serverDeletedPolicy'; - payload: { id: string; success: boolean }; -} - -interface ServerReturnedPolicyAgentsSummaryForDeleteFailure { - type: 'serverReturnedPolicyAgentsSummaryForDeleteFailure'; - payload: ServerApiError; -} - -interface ServerReturnedPolicyAgentsSummaryForDelete { - type: 'serverReturnedPolicyAgentsSummaryForDelete'; - payload: { agentStatusSummary: GetAgentStatusResponse['results'] }; -} - -interface ServerReturnedEndpointPackageInfo { - type: 'serverReturnedEndpointPackageInfo'; - payload: GetPackagesResponse['response'][0]; -} - -export type PolicyListAction = - | ServerReturnedPolicyListData - | ServerFailedToReturnPolicyListData - | UserClickedPolicyListDeleteButton - | ServerDeletedPolicyFailure - | ServerDeletedPolicy - | UserOpenedPolicyListDeleteModal - | ServerReturnedPolicyAgentsSummaryForDeleteFailure - | ServerReturnedPolicyAgentsSummaryForDelete - | ServerReturnedEndpointPackageInfo; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/index.test.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/index.test.ts deleted file mode 100644 index 524c44406bd33..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/index.test.ts +++ /dev/null @@ -1,277 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { PolicyListState } from '../../types'; -import { Store, applyMiddleware, createStore } from 'redux'; - -import { coreMock } from '../../../../../../../../../src/core/public/mocks'; -import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../../../../../../../fleet/common'; - -import { policyListReducer } from './reducer'; -import { policyListMiddlewareFactory } from './middleware'; - -import { - isOnPolicyListPage, - selectIsLoading, - urlSearchParams, - selectIsDeleting, - endpointPackageVersion, -} from './selectors'; -import { DepsStartMock, depsStartMock } from '../../../../../common/mock/endpoint'; -import { setPolicyListApiMockImplementation } from './test_mock_utils'; -import { INGEST_API_PACKAGE_POLICIES } from './services/ingest'; -import { - createSpyMiddleware, - MiddlewareActionSpyHelper, -} from '../../../../../common/store/test_utils'; -import { getPoliciesPath } from '../../../../common/routing'; - -describe('policy list store concerns', () => { - const policyListPathUrl = getPoliciesPath(); - let fakeCoreStart: ReturnType; - let depsStart: DepsStartMock; - let store: Store; - let waitForAction: MiddlewareActionSpyHelper['waitForAction']; - - beforeEach(() => { - fakeCoreStart = coreMock.createStart({ basePath: '/mock' }); - depsStart = depsStartMock(); - setPolicyListApiMockImplementation(fakeCoreStart.http); - let actionSpyMiddleware; - ({ actionSpyMiddleware, waitForAction } = createSpyMiddleware()); - - store = createStore( - policyListReducer, - undefined, - applyMiddleware(policyListMiddlewareFactory(fakeCoreStart, depsStart), actionSpyMiddleware) - ); - }); - - it('it does nothing on `userChangedUrl` if pathname is NOT `/policy`', async () => { - const state = store.getState(); - expect(isOnPolicyListPage(state)).toBe(false); - store.dispatch({ - type: 'userChangedUrl', - payload: { - pathname: '/foo', - search: '', - hash: '', - }, - }); - expect(store.getState()).toEqual(state); - }); - - it('it reports `isOnPolicyListPage` correctly when router pathname is `/policy`', async () => { - store.dispatch({ - type: 'userChangedUrl', - payload: { - pathname: policyListPathUrl, - search: '', - hash: '', - }, - }); - expect(isOnPolicyListPage(store.getState())).toBe(true); - }); - - it('it sets `isLoading` when `userChangedUrl`', async () => { - expect(selectIsLoading(store.getState())).toBe(false); - store.dispatch({ - type: 'userChangedUrl', - payload: { - pathname: policyListPathUrl, - search: '', - hash: '', - }, - }); - expect(selectIsLoading(store.getState())).toBe(true); - await waitForAction('serverReturnedPolicyListData'); - expect(selectIsLoading(store.getState())).toBe(false); - }); - - it('it sets `isDeleting` when `userClickedPolicyListDeleteButton`', async () => { - expect(selectIsDeleting(store.getState())).toBe(false); - store.dispatch({ - type: 'userClickedPolicyListDeleteButton', - payload: { - policyId: '123', - }, - }); - expect(selectIsDeleting(store.getState())).toBe(true); - await waitForAction('serverDeletedPolicy'); - expect(selectIsDeleting(store.getState())).toBe(false); - }); - - it('it sets refreshes policy data when `serverDeletedPolicy`', async () => { - expect(selectIsLoading(store.getState())).toBe(false); - store.dispatch({ - type: 'serverDeletedPolicy', - payload: { - policyId: '', - success: true, - }, - }); - expect(selectIsLoading(store.getState())).toBe(true); - await waitForAction('serverReturnedPolicyListData'); - expect(selectIsLoading(store.getState())).toBe(false); - }); - - it('it resets state on `userChangedUrl` and pathname is NOT `/policy`', async () => { - store.dispatch({ - type: 'userChangedUrl', - payload: { - pathname: policyListPathUrl, - search: '', - hash: '', - }, - }); - await waitForAction('serverReturnedPolicyListData'); - store.dispatch({ - type: 'userChangedUrl', - payload: { - pathname: '/foo', - search: '', - hash: '', - }, - }); - expect(store.getState()).toEqual({ - apiError: undefined, - location: undefined, - policyItems: [], - isLoading: false, - isDeleting: false, - deleteStatus: undefined, - endpointPackageInfo: undefined, - pageIndex: 0, - pageSize: 10, - total: 0, - agentStatusSummary: { - error: 0, - events: 0, - offline: 0, - online: 0, - total: 0, - other: 0, - }, - }); - }); - it('uses default pagination params when not included in url', async () => { - store.dispatch({ - type: 'userChangedUrl', - payload: { - pathname: policyListPathUrl, - search: '', - hash: '', - }, - }); - await waitForAction('serverReturnedPolicyListData'); - expect(fakeCoreStart.http.get).toHaveBeenCalledWith(INGEST_API_PACKAGE_POLICIES, { - query: { - kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name: endpoint`, - page: 1, - perPage: 10, - }, - }); - }); - - describe('when url contains search params', () => { - const dispatchUserChangedUrl = (searchParams: string = '') => - store.dispatch({ - type: 'userChangedUrl', - payload: { - pathname: policyListPathUrl, - search: searchParams, - hash: '', - }, - }); - - it('uses pagination params from url', async () => { - dispatchUserChangedUrl('?page_size=50&page_index=0'); - await waitForAction('serverReturnedPolicyListData'); - expect(fakeCoreStart.http.get).toHaveBeenCalledWith(INGEST_API_PACKAGE_POLICIES, { - query: { - kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name: endpoint`, - page: 1, - perPage: 50, - }, - }); - }); - it('uses defaults for params not in url', async () => { - dispatchUserChangedUrl('?page_index=99'); - expect(urlSearchParams(store.getState())).toEqual({ - page_index: 99, - page_size: 10, - }); - dispatchUserChangedUrl('?page_size=50'); - expect(urlSearchParams(store.getState())).toEqual({ - page_index: 0, - page_size: 50, - }); - }); - it('accepts only positive numbers for page_index and page_size', async () => { - dispatchUserChangedUrl('?page_size=-50&page_index=-99'); - await waitForAction('serverReturnedPolicyListData'); - expect(fakeCoreStart.http.get).toHaveBeenCalledWith(INGEST_API_PACKAGE_POLICIES, { - query: { - kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name: endpoint`, - page: 1, - perPage: 10, - }, - }); - }); - it('it ignores non-numeric values for page_index and page_size', async () => { - dispatchUserChangedUrl('?page_size=fifty&page_index=ten'); - await waitForAction('serverReturnedPolicyListData'); - expect(fakeCoreStart.http.get).toHaveBeenCalledWith(INGEST_API_PACKAGE_POLICIES, { - query: { - kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name: endpoint`, - page: 1, - perPage: 10, - }, - }); - }); - it('accepts only known values for `page_size`', async () => { - dispatchUserChangedUrl('?page_size=300&page_index=10'); - await waitForAction('serverReturnedPolicyListData'); - expect(fakeCoreStart.http.get).toHaveBeenCalledWith(INGEST_API_PACKAGE_POLICIES, { - query: { - kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name: endpoint`, - page: 11, - perPage: 10, - }, - }); - }); - it(`ignores unknown url search params`, async () => { - dispatchUserChangedUrl('?page_size=20&page_index=10&foo=bar'); - expect(urlSearchParams(store.getState())).toEqual({ - page_index: 10, - page_size: 20, - }); - }); - it(`uses last param value if param is defined multiple times`, async () => { - dispatchUserChangedUrl('?page_size=20&page_size=50&page_index=20&page_index=40'); - expect(urlSearchParams(store.getState())).toEqual({ - page_index: 40, - page_size: 50, - }); - }); - - it('should load package information only if not already in state', async () => { - dispatchUserChangedUrl('?page_size=10&page_index=10'); - await waitForAction('serverReturnedEndpointPackageInfo'); - expect(endpointPackageVersion(store.getState())).toEqual('0.5.0'); - fakeCoreStart.http.get.mockClear(); - dispatchUserChangedUrl('?page_size=10&page_index=11'); - expect(fakeCoreStart.http.get).toHaveBeenCalledWith(INGEST_API_PACKAGE_POLICIES, { - query: { - kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name: endpoint`, - page: 12, - perPage: 10, - }, - }); - expect(endpointPackageVersion(store.getState())).toEqual('0.5.0'); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/index.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/index.ts deleted file mode 100644 index e09f80883d888..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/index.ts +++ /dev/null @@ -1,21 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { PolicyListState } from '../../types'; -import { ImmutableReducer } from '../../../../../common/store'; -import { AppAction } from '../../../../../common/store/actions'; -import { Immutable } from '../../../../../../common/endpoint/types'; -export { policyListReducer } from './reducer'; -export { PolicyListAction } from './action'; -export { policyListMiddlewareFactory } from './middleware'; - -export interface EndpointPolicyListStatePluginState { - policyList: Immutable; -} - -export interface EndpointPolicyListStatePluginReducer { - policyList: ImmutableReducer; -} diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/middleware.ts deleted file mode 100644 index e57eb0e32e516..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/middleware.ts +++ /dev/null @@ -1,124 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { GetPolicyListResponse, PolicyListState } from '../../types'; -import { - sendGetEndpointSpecificPackagePolicies, - sendDeletePackagePolicy, - sendGetFleetAgentStatusForPolicy, - sendGetEndpointSecurityPackage, -} from './services/ingest'; -import { endpointPackageInfo, isOnPolicyListPage, urlSearchParams } from './selectors'; -import { ImmutableMiddlewareFactory } from '../../../../../common/store'; -import { initialPolicyListState } from './reducer'; -import { - DeletePackagePoliciesResponse, - DeletePackagePoliciesRequest, - GetAgentStatusResponse, -} from '../../../../../../../fleet/common'; - -export const policyListMiddlewareFactory: ImmutableMiddlewareFactory = ( - coreStart -) => { - const http = coreStart.http; - - return ({ getState, dispatch }) => (next) => async (action) => { - next(action); - - const state = getState(); - - if ( - (action.type === 'userChangedUrl' && isOnPolicyListPage(state)) || - action.type === 'serverDeletedPolicy' - ) { - if (!endpointPackageInfo(state)) { - // We only need the package information to retrieve the version number, - // and even if we don't have the version, the UI is still ok because we - // handle that condition. Because of this, we retrieve the package information - // in a non-blocking way here and also ignore any API failures (only log it - // to the console) - sendGetEndpointSecurityPackage(http) - .then((packageInfo) => { - dispatch({ - type: 'serverReturnedEndpointPackageInfo', - payload: packageInfo, - }); - }) - .catch((error) => { - // eslint-disable-next-line no-console - console.error(error); - }); - } - - const { page_index: pageIndex, page_size: pageSize } = urlSearchParams(state); - let response: GetPolicyListResponse; - - try { - response = await sendGetEndpointSpecificPackagePolicies(http, { - query: { - perPage: pageSize, - page: pageIndex + 1, - }, - }); - } catch (err) { - dispatch({ - type: 'serverFailedToReturnPolicyListData', - payload: err.body ?? err, - }); - return; - } - - dispatch({ - type: 'serverReturnedPolicyListData', - payload: { - policyItems: response ? response.items : initialPolicyListState().policyItems, - pageIndex, - pageSize, - total: response ? response.total : initialPolicyListState().total, - }, - }); - } else if (action.type === 'userClickedPolicyListDeleteButton') { - const { policyId } = action.payload; - const packagePolicyIds: DeletePackagePoliciesRequest['body']['packagePolicyIds'] = [policyId]; - let apiResponse: DeletePackagePoliciesResponse; - try { - apiResponse = await sendDeletePackagePolicy(http, { body: { packagePolicyIds } }); - } catch (err) { - dispatch({ - type: 'serverDeletedPolicyFailure', - payload: err.body ?? err, - }); - return; - } - - dispatch({ - type: 'serverDeletedPolicy', - payload: { - id: apiResponse ? apiResponse[0].id : '', - success: true, - }, - }); - } else if (action.type === 'userOpenedPolicyListDeleteModal') { - const { agentPolicyId } = action.payload; - let apiResponse: GetAgentStatusResponse; - try { - apiResponse = await sendGetFleetAgentStatusForPolicy(http, agentPolicyId); - } catch (err) { - dispatch({ - type: 'serverReturnedPolicyAgentsSummaryForDeleteFailure', - payload: err.body ?? err, - }); - return; - } - dispatch({ - type: 'serverReturnedPolicyAgentsSummaryForDelete', - payload: { - agentStatusSummary: apiResponse.results, - }, - }); - } - }; -}; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/reducer.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/reducer.ts deleted file mode 100644 index 53954449ab9c3..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/reducer.ts +++ /dev/null @@ -1,132 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { isOnPolicyListPage } from './selectors'; -import { ImmutableReducer } from '../../../../../common/store'; -import { AppAction } from '../../../../../common/store/actions'; -import { Immutable } from '../../../../../../common/endpoint/types'; -import { PolicyListState } from '../../types'; - -/** - * Return the initial state. - * In case `state` was mutated, we return a fresh initial state object. - */ -export const initialPolicyListState: () => Immutable = () => ({ - policyItems: [], - endpointPackageInfo: undefined, - isLoading: false, - isDeleting: false, - deleteStatus: undefined, - apiError: undefined, - pageIndex: 0, - pageSize: 10, - total: 0, - location: undefined, - agentStatusSummary: { - error: 0, - events: 0, - offline: 0, - online: 0, - total: 0, - other: 0, - }, -}); - -export const policyListReducer: ImmutableReducer = ( - state = initialPolicyListState(), - action -) => { - if (action.type === 'serverReturnedPolicyListData') { - return { - ...state, - ...action.payload, - isLoading: false, - isDeleting: false, - }; - } - - if (action.type === 'serverFailedToReturnPolicyListData') { - return { - ...state, - apiError: action.payload, - isLoading: false, - isDeleting: false, - }; - } - - if (action.type === 'serverDeletedPolicyFailure') { - return { - ...state, - ...action.payload, - isLoading: false, - isDeleting: false, - }; - } - - if (action.type === 'serverDeletedPolicy') { - return { - ...state, - deleteStatus: action.payload.success, - isLoading: true, - isDeleting: false, - }; - } - - if (action.type === 'userClickedPolicyListDeleteButton') { - return { - ...state, - isLoading: false, - isDeleting: true, - }; - } - - if (action.type === 'serverReturnedPolicyAgentsSummaryForDelete') { - return { - ...state, - ...action.payload, - }; - } - - if (action.type === 'serverReturnedPolicyAgentsSummaryForDeleteFailure') { - return { - ...state, - ...action.payload, - }; - } - - if (action.type === 'serverReturnedEndpointPackageInfo') { - return { - ...state, - endpointPackageInfo: action.payload, - }; - } - - if (action.type === 'userChangedUrl') { - const newState: Immutable = { - ...state, - location: action.payload, - }; - const isCurrentlyOnListPage = isOnPolicyListPage(newState); - const wasPreviouslyOnListPage = isOnPolicyListPage(state); - - // If on the current page, then return new state with location information - // Also adjust some state if user is just entering the policy list view - if (isCurrentlyOnListPage) { - if (!wasPreviouslyOnListPage) { - return { - ...newState, - apiError: undefined, - isLoading: true, - isDeleting: false, - }; - } - return newState; - } - return initialPolicyListState(); - } - - return state; -}; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/selectors.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/selectors.ts deleted file mode 100644 index ce57d238d7581..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/selectors.ts +++ /dev/null @@ -1,100 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { createSelector } from 'reselect'; -import { parse } from 'query-string'; -import { matchPath } from 'react-router-dom'; -import { PolicyListState, PolicyListUrlSearchParams } from '../../types'; -import { Immutable } from '../../../../../../common/endpoint/types'; -import { MANAGEMENT_ROUTING_POLICIES_PATH } from '../../../../common/constants'; - -const PAGE_SIZES = Object.freeze([10, 20, 50]); - -export const selectPolicyItems = (state: Immutable) => state.policyItems; - -export const selectPageIndex = (state: Immutable) => state.pageIndex; - -export const selectPageSize = (state: Immutable) => state.pageSize; - -export const selectTotal = (state: Immutable) => state.total; - -export const selectIsLoading = (state: Immutable) => state.isLoading; - -export const selectApiError = (state: Immutable) => state.apiError; - -export const selectIsDeleting = (state: Immutable) => state.isDeleting; - -export const selectDeleteStatus = (state: Immutable) => state.deleteStatus; - -export const selectAgentStatusSummary = (state: Immutable) => - state.agentStatusSummary; - -export const isOnPolicyListPage = (state: Immutable) => { - return ( - matchPath(state.location?.pathname ?? '', { - path: MANAGEMENT_ROUTING_POLICIES_PATH, - exact: true, - }) !== null - ); -}; - -const routeLocation = (state: Immutable) => state.location; - -/** - * Returns the supported URL search params, populated with defaults if none where present in the URL - */ -export const urlSearchParams: ( - state: Immutable -) => PolicyListUrlSearchParams = createSelector(routeLocation, (location) => { - const searchParams = { - page_index: 0, - page_size: 10, - }; - if (!location) { - return searchParams; - } - - const query = parse(location.search); - - // Search params can appear multiple times in the URL, in which case the value for them, - // once parsed, would be an array. In these case, we take the last value defined - searchParams.page_index = Number( - (Array.isArray(query.page_index) - ? query.page_index[query.page_index.length - 1] - : query.page_index) ?? 0 - ); - searchParams.page_size = Number( - (Array.isArray(query.page_size) - ? query.page_size[query.page_size.length - 1] - : query.page_size) ?? 10 - ); - - // If pageIndex is not a valid positive integer, set it to 0 - if (!Number.isFinite(searchParams.page_index) || searchParams.page_index < 0) { - searchParams.page_index = 0; - } - - // if pageSize is not one of the expected page sizes, reset it to 10 - if (!PAGE_SIZES.includes(searchParams.page_size)) { - searchParams.page_size = 10; - } - - return searchParams; -}); - -/** - * Returns package information for Endpoint - * @param state - */ -export const endpointPackageInfo = (state: Immutable) => state.endpointPackageInfo; - -/** - * Returns the version number for the endpoint package. - */ -export const endpointPackageVersion = createSelector( - endpointPackageInfo, - (info) => info?.version ?? undefined -); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/services/ingest.test.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/services/ingest.test.ts similarity index 96% rename from x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/services/ingest.test.ts rename to x-pack/plugins/security_solution/public/management/pages/policy/store/services/ingest.test.ts index 43a12868368c5..5e1e6706488c0 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/services/ingest.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/services/ingest.test.ts @@ -10,8 +10,8 @@ import { sendGetEndpointSecurityPackage, sendGetEndpointSpecificPackagePolicies, } from './ingest'; -import { httpServiceMock } from '../../../../../../../../../../src/core/public/mocks'; -import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../../../../../../../../fleet/common'; +import { httpServiceMock } from '../../../../../../../../../src/core/public/mocks'; +import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../../../../../../../fleet/common'; import { policyListApiPathHandlers } from '../test_mock_utils'; describe('ingest service', () => { diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/services/ingest.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/services/ingest.ts similarity index 96% rename from x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/services/ingest.ts rename to x-pack/plugins/security_solution/public/management/pages/policy/store/services/ingest.ts index a241f8d1c4556..ad37f1217de70 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/services/ingest.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/services/ingest.ts @@ -15,9 +15,9 @@ import { GetPackagesResponse, GetAgentPoliciesRequest, GetAgentPoliciesResponse, -} from '../../../../../../../../fleet/common'; -import { GetPolicyListResponse, GetPolicyResponse, UpdatePolicyResponse } from '../../../types'; -import { NewPolicyData } from '../../../../../../../common/endpoint/types'; +} from '../../../../../../../fleet/common'; +import { GetPolicyListResponse, GetPolicyResponse, UpdatePolicyResponse } from '../../types'; +import { NewPolicyData } from '../../../../../../common/endpoint/types'; const INGEST_API_ROOT = `/api/fleet`; export const INGEST_API_PACKAGE_POLICIES = `${INGEST_API_ROOT}/package_policies`; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/test_mock_utils.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/test_mock_utils.ts similarity index 67% rename from x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/test_mock_utils.ts rename to x-pack/plugins/security_solution/public/management/pages/policy/store/test_mock_utils.ts index 3c8b1f913c868..6e12f325f83f0 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/test_mock_utils.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/test_mock_utils.ts @@ -4,36 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { HttpStart } from 'kibana/public'; import { INGEST_API_EPM_PACKAGES, INGEST_API_PACKAGE_POLICIES } from './services/ingest'; -import { EndpointDocGenerator } from '../../../../../../common/endpoint/generate_data'; -import { GetPolicyListResponse } from '../../types'; -import { GetPackagesResponse } from '../../../../../../../fleet/common'; +import { EndpointDocGenerator } from '../../../../../common/endpoint/generate_data'; +import { GetPolicyListResponse } from '../types'; +import { GetPackagesResponse } from '../../../../../../fleet/common'; const generator = new EndpointDocGenerator('policy-list'); -/** - * It sets the mock implementation on the necessary http methods to support the policy list view - * @param mockedHttpService - * @param totalPolicies - */ -export const setPolicyListApiMockImplementation = ( - mockedHttpService: jest.Mocked, - totalPolicies: number = 1 -): void => { - const policyApiHandlers = policyListApiPathHandlers(totalPolicies); - - mockedHttpService.get.mockImplementation(async (...args) => { - const [path] = args; - if (typeof path === 'string') { - if (policyApiHandlers[path]) { - return policyApiHandlers[path](); - } - } - return Promise.reject(new Error(`MOCK: unknown policy list api: ${path}`)); - }); -}; - /** * Returns the response body for a call to get the list of Policies * @param options diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/components/antivirus_registration_form/index.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/components/antivirus_registration_form/index.tsx index 072f588663c5a..a533530cfe934 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/components/antivirus_registration_form/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/components/antivirus_registration_form/index.tsx @@ -18,21 +18,21 @@ const TRANSLATIONS: Readonly<{ [K in 'title' | 'description' | 'label']: string title: i18n.translate( 'xpack.securitySolution.endpoint.policy.details.antivirusRegistration.type', { - defaultMessage: 'Register as anti-virus', + defaultMessage: 'Register as antivirus', } ), description: i18n.translate( 'xpack.securitySolution.endpoint.policy.details.antivirusRegistration.explanation', { defaultMessage: - 'Toggle on to register Elastic as an official Anti-Virus solution for Windows OS. ' + + 'Toggle on to register Elastic as an official Antivirus solution for Windows OS. ' + 'This will also disable Windows Defender.', } ), label: i18n.translate( 'xpack.securitySolution.endpoint.policy.details.antivirusRegistration.toggle', { - defaultMessage: 'Register as anti-virus', + defaultMessage: 'Register as antivirus', } ), }; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/index.ts b/x-pack/plugins/security_solution/public/management/pages/policy/view/index.ts index ce942205b1620..e61067cbe57fc 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/index.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/index.ts @@ -4,6 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -export * from './policy_list'; export * from './policy_details'; export * from './policy_advanced'; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx index e9c13b23834b1..1280f1c351c2b 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx @@ -12,7 +12,7 @@ import '../../../../common/mock/match_media.ts'; import { EndpointDocGenerator } from '../../../../../common/endpoint/generate_data'; import { AppContextTestRender, createAppRootMockRenderer } from '../../../../common/mock/endpoint'; import { getPolicyDetailPath, getEndpointListPath } from '../../../common/routing'; -import { policyListApiPathHandlers } from '../store/policy_list/test_mock_utils'; +import { policyListApiPathHandlers } from '../store/test_mock_utils'; import { licenseService } from '../../../../common/hooks/use_license'; jest.mock('../../../../common/components/link_to'); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_hooks.ts b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_hooks.ts index 97436064eebe2..6438c43bf7b5a 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_hooks.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_hooks.ts @@ -5,28 +5,13 @@ */ import { useSelector } from 'react-redux'; -import { PolicyListState, PolicyDetailsState } from '../types'; +import { PolicyDetailsState } from '../types'; import { State } from '../../../../common/store'; import { MANAGEMENT_STORE_GLOBAL_NAMESPACE, MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE, - MANAGEMENT_STORE_POLICY_LIST_NAMESPACE, } from '../../../common/constants'; -/** - * Narrows global state down to the PolicyListState before calling the provided Policy List Selector - * @param selector - */ -export function usePolicyListSelector(selector: (state: PolicyListState) => TSelected) { - return useSelector((state: State) => { - return selector( - state[MANAGEMENT_STORE_GLOBAL_NAMESPACE][ - MANAGEMENT_STORE_POLICY_LIST_NAMESPACE - ] as PolicyListState - ); - }); -} - /** * Narrows global state down to the PolicyDetailsState before calling the provided Policy Details Selector * @param selector diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.test.tsx deleted file mode 100644 index 4fe7a496276f3..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.test.tsx +++ /dev/null @@ -1,77 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { PolicyList } from './index'; -import '../../../../common/mock/match_media.ts'; -import { AppContextTestRender, createAppRootMockRenderer } from '../../../../common/mock/endpoint'; -import { setPolicyListApiMockImplementation } from '../store/policy_list/test_mock_utils'; - -jest.mock('../../../../common/components/link_to'); - -// Skipping these test now that the Policy List has been hidden -describe.skip('when on the policies page', () => { - let render: () => ReturnType; - let history: AppContextTestRender['history']; - let coreStart: AppContextTestRender['coreStart']; - let middlewareSpy: AppContextTestRender['middlewareSpy']; - - beforeEach(() => { - const mockedContext = createAppRootMockRenderer(); - ({ history, coreStart, middlewareSpy } = mockedContext); - render = () => mockedContext.render(); - }); - - it('should NOT display timeline', async () => { - const renderResult = render(); - const timelineFlyout = await renderResult.queryByTestId('flyoutOverlay'); - expect(timelineFlyout).toBeNull(); - }); - - it('should show the empty state', async () => { - const renderResult = render(); - const table = await renderResult.findByTestId('emptyPolicyTable'); - expect(table).not.toBeNull(); - }); - - it('should display the instructions', async () => { - const renderResult = render(); - const onboardingSteps = await renderResult.findByTestId('policyOnboardingInstructions'); - expect(onboardingSteps).not.toBeNull(); - }); - - describe('when list data loads', () => { - let firstPolicyID: string; - const renderList = async () => { - const renderResult = render(); - history.push('/policy'); - await Promise.all([ - middlewareSpy - .waitForAction('serverReturnedPolicyListData') - .then((action) => (firstPolicyID = action.payload.policyItems[0].id)), - // middlewareSpy.waitForAction('serverReturnedAgentPolicyListData'), - ]); - return renderResult; - }; - - beforeEach(async () => { - setPolicyListApiMockImplementation(coreStart.http, 3); - }); - - it('should display rows in the table', async () => { - const renderResult = await renderList(); - const rows = await renderResult.findAllByRole('row'); - expect(rows).toHaveLength(4); - }); - - it('should display policy name value as a link', async () => { - const renderResult = await renderList(); - const policyNameLink = (await renderResult.findAllByTestId('policyNameLink'))[0]; - expect(policyNameLink).not.toBeNull(); - expect(policyNameLink.getAttribute('href')).toContain(`policy/${firstPolicyID}`); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.tsx deleted file mode 100644 index 09c1fc1915d02..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.tsx +++ /dev/null @@ -1,524 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { useCallback, useEffect, useMemo, CSSProperties, useState } from 'react'; -import { - EuiBasicTable, - EuiText, - EuiSpacer, - EuiFlexGroup, - EuiFlexItem, - EuiTableFieldDataColumnType, - EuiLink, - EuiPopover, - EuiContextMenuPanelProps, - EuiContextMenuItem, - EuiButtonIcon, - EuiContextMenuPanel, - EuiOverlayMask, - EuiConfirmModal, - EuiCallOut, - EuiButton, - EuiHorizontalRule, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { useDispatch } from 'react-redux'; -import { useLocation, useHistory } from 'react-router-dom'; -import { createStructuredSelector } from 'reselect'; -import styled from 'styled-components'; -import { ApplicationStart } from 'src/core/public'; -import { CreateStructuredSelector } from '../../../../common/store'; -import * as selectors from '../store/policy_list/selectors'; -import { usePolicyListSelector } from './policy_hooks'; -import { PolicyListAction } from '../store/policy_list'; -import { useToasts } from '../../../../common/lib/kibana'; -import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; -import { Immutable, PolicyData } from '../../../../../common/endpoint/types'; -import { useNavigateByRouterEventHandler } from '../../../../common/hooks/endpoint/use_navigate_by_router_event_handler'; -import { LinkToApp } from '../../../../common/components/endpoint/link_to_app'; -import { PolicyEmptyState } from '../../../components/management_empty_state'; -import { FormattedDateAndTime } from '../../../../common/components/endpoint/formatted_date_time'; -import { SecurityPageName } from '../../../../app/types'; -import { useFormatUrl } from '../../../../common/components/link_to'; -import { getPolicyDetailPath, getPoliciesPath } from '../../../common/routing'; -import { useNavigateToAppEventHandler } from '../../../../common/hooks/endpoint/use_navigate_to_app_event_handler'; -import { CreatePackagePolicyRouteState } from '../../../../../../fleet/public'; -import { MANAGEMENT_APP_ID } from '../../../common/constants'; -import { AdministrationListPage } from '../../../components/administration_list_page'; - -interface TableChangeCallbackArguments { - page: { index: number; size: number }; -} - -interface PackageData { - name: string; - title: string; - version: string; -} - -const NO_WRAP_TRUNCATE_STYLE: CSSProperties = Object.freeze({ - overflow: 'hidden', - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', -}); - -const DangerEuiContextMenuItem = styled(EuiContextMenuItem)` - color: ${(props) => props.theme.eui.euiColorDangerText}; -`; - -// eslint-disable-next-line react/display-name -export const TableRowActions = React.memo<{ items: EuiContextMenuPanelProps['items'] }>( - ({ items }) => { - const [isOpen, setIsOpen] = useState(false); - const handleCloseMenu = useCallback(() => setIsOpen(false), [setIsOpen]); - const handleToggleMenu = useCallback(() => setIsOpen(!isOpen), [isOpen]); - - return ( - - } - isOpen={isOpen} - closePopover={handleCloseMenu} - repositionOnScroll - > - - - ); - } -); - -const PolicyLink: React.FC<{ name: string; route: string; href: string }> = ({ - name, - route, - href, -}) => { - const clickHandler = useNavigateByRouterEventHandler(route); - return ( - // eslint-disable-next-line @elastic/eui/href-or-on-click - - {name} - - ); -}; - -const selector = (createStructuredSelector as CreateStructuredSelector)(selectors); -export const PolicyList = React.memo(() => { - const { services } = useKibana<{ application: ApplicationStart }>(); - const toasts = useToasts(); - const history = useHistory(); - const location = useLocation(); - const { formatUrl, search } = useFormatUrl(SecurityPageName.administration); - - const [showDelete, setShowDelete] = useState(false); - const [policyIdToDelete, setPolicyIdToDelete] = useState(''); - - const dispatch = useDispatch<(action: PolicyListAction) => void>(); - const { - selectPolicyItems: policyItems, - selectPageIndex: pageIndex, - selectPageSize: pageSize, - selectTotal: totalItemCount, - selectIsLoading: loading, - selectApiError: apiError, - selectIsDeleting: isDeleting, - selectDeleteStatus: deleteStatus, - selectAgentStatusSummary: agentStatusSummary, - endpointPackageVersion, - } = usePolicyListSelector(selector); - - const handleCreatePolicyClick = useNavigateToAppEventHandler( - 'fleet', - { - // We redirect to Ingest's Integaration page if we can't get the package version, and - // to the Integration Endpoint Package Add Integration if we have package information. - // Also, - // We pass along soem state information so that the Ingest page can change the behaviour - // of the cancel and submit buttons and redirect the user back to endpoint policy - path: `#/integrations${ - endpointPackageVersion ? `/endpoint-${endpointPackageVersion}/add-integration` : '' - }`, - state: { - onCancelNavigateTo: [MANAGEMENT_APP_ID, { path: getPoliciesPath() }], - onCancelUrl: formatUrl(getPoliciesPath()), - onSaveNavigateTo: [MANAGEMENT_APP_ID, { path: getPoliciesPath() }], - }, - } - ); - - useEffect(() => { - if (apiError) { - toasts.addDanger({ - title: apiError.error, - text: apiError.message, - }); - } - }, [apiError, dispatch, toasts]); - - // Handle showing update statuses - useEffect(() => { - if (deleteStatus !== undefined) { - if (deleteStatus === true) { - setPolicyIdToDelete(''); - setShowDelete(false); - toasts.addSuccess({ - title: i18n.translate('xpack.securitySolution.endpoint.policyList.deleteSuccessToast', { - defaultMessage: 'Success!', - }), - text: i18n.translate( - 'xpack.securitySolution.endpoint.policyList.deleteSuccessToastDetails', - { - defaultMessage: 'Policy has been deleted.', - } - ), - }); - } else { - toasts.addDanger({ - title: i18n.translate('xpack.securitySolution.endpoint.policyList.deleteFailedToast', { - defaultMessage: 'Failed!', - }), - text: i18n.translate('xpack.securitySolution.endpoint.policyList.deleteFailedToastBody', { - defaultMessage: 'Failed to delete policy', - }), - }); - } - } - }, [toasts, deleteStatus]); - - const paginationSetup = useMemo(() => { - return { - pageIndex, - pageSize, - totalItemCount, - pageSizeOptions: [10, 20, 50], - hidePerPageOptions: false, - }; - }, [pageIndex, pageSize, totalItemCount]); - - const handleTableChange = useCallback( - ({ page: { index, size } }: TableChangeCallbackArguments) => { - history.push(`${location.pathname}?page_index=${index}&page_size=${size}`); - }, - [history, location.pathname] - ); - - const handleDeleteOnClick = useCallback( - ({ policyId, agentPolicyId }: { policyId: string; agentPolicyId: string }) => { - dispatch({ - type: 'userOpenedPolicyListDeleteModal', - payload: { - agentPolicyId, - }, - }); - setPolicyIdToDelete(policyId); - setShowDelete(true); - }, - [dispatch] - ); - - const handleDeleteConfirmation = useCallback( - ({ policyId }: { policyId: string }) => { - dispatch({ - type: 'userClickedPolicyListDeleteButton', - payload: { - policyId, - }, - }); - }, - [dispatch] - ); - - const handleDeleteCancel = useCallback(() => { - setShowDelete(false); - }, []); - - const columns: Array>> = useMemo( - () => [ - { - field: 'name', - name: i18n.translate('xpack.securitySolution.endpoint.policyList.nameField', { - defaultMessage: 'Policy Name', - }), - // eslint-disable-next-line react/display-name - render: (name: string, item: Immutable) => { - const routePath = getPolicyDetailPath(item.id, search); - const routeUrl = formatUrl(routePath); - return ( - - - - - - - - - - - ); - }, - }, - { - field: 'created_by', - name: i18n.translate('xpack.securitySolution.endpoint.policyList.createdBy', { - defaultMessage: 'Created By', - }), - truncateText: true, - }, - { - field: 'created_at', - name: i18n.translate('xpack.securitySolution.endpoint.policyList.createdAt', { - defaultMessage: 'Created Date', - }), - render(createdAt: string) { - return ; - }, - }, - { - field: 'updated_by', - name: i18n.translate('xpack.securitySolution.endpoint.policyList.updatedBy', { - defaultMessage: 'Last Updated By', - }), - truncateText: true, - }, - { - field: 'updated_at', - name: i18n.translate('xpack.securitySolution.endpoint.policyList.updatedAt', { - defaultMessage: 'Last Updated', - }), - render(updatedAt: string) { - return ; - }, - }, - { - field: 'package', - name: i18n.translate('xpack.securitySolution.endpoint.policyList.versionFieldLabel', { - defaultMessage: 'Version', - }), - render(pkg: Immutable) { - return i18n.translate('xpack.securitySolution.endpoint.policyList.versionField', { - defaultMessage: 'v{version}', - values: { - version: pkg.version, - }, - }); - }, - }, - { - field: '', - name: 'Actions', - actions: [ - { - // eslint-disable-next-line react/display-name - render: (item: Immutable) => { - return ( - - - - - , - { - handleDeleteOnClick({ agentPolicyId: item.policy_id, policyId: item.id }); - }} - > - - , - ]} - /> - ); - }, - }, - ], - }, - ], - [services.application, handleDeleteOnClick, formatUrl, search] - ); - - const bodyContent = useMemo(() => { - return policyItems && policyItems.length > 0 ? ( - - ) : ( - - ); - }, [policyItems, loading, columns, handleCreatePolicyClick, handleTableChange, paginationSetup]); - - return ( - <> - {showDelete && ( - { - handleDeleteConfirmation({ policyId: policyIdToDelete }); - }} - /> - )} - - } - subtitle={ - - } - actions={ - - - - } - > - {policyItems && policyItems.length > 0 && ( - <> - - - - - - )} - {bodyContent} - - - ); -}); - -PolicyList.displayName = 'PolicyList'; - -const ConfirmDelete = React.memo<{ - hostCount: number; - isDeleting: boolean; - onConfirm: () => void; - onCancel: () => void; -}>(({ hostCount, isDeleting, onCancel, onConfirm }) => { - return ( - - - ) : ( - - ) - } - confirmButtonDisabled={isDeleting} - cancelButtonText={i18n.translate( - 'xpack.securitySolution.endpoint.policyList.deleteConfirm.cancelButtonTitle', - { - defaultMessage: 'Cancel', - } - )} - > - {hostCount > 0 && ( - <> - - - - - - )} -

    - -

    -
    -
    - ); -}); - -ConfirmDelete.displayName = 'ConfirmDelete'; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts index 44f43b90bdd0f..735e63f8e084b 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts @@ -179,7 +179,9 @@ describe('middleware', () => { const service = createTrustedAppsServiceMock(); const { store, spyMiddleware } = createStoreSetup(service); - service.getTrustedAppsList.mockRejectedValue(createServerApiError('Internal Server Error')); + service.getTrustedAppsList.mockRejectedValue({ + body: createServerApiError('Internal Server Error'), + }); store.dispatch(createUserChangedUrlAction('/trusted_apps', '?page_index=2&page_size=50')); @@ -315,7 +317,7 @@ describe('middleware', () => { const { store, spyMiddleware } = createStoreSetup(service); service.getTrustedAppsList.mockResolvedValue(getTrustedAppsListResponse); - service.deleteTrustedApp.mockRejectedValue(notFoundError); + service.deleteTrustedApp.mockRejectedValue({ body: notFoundError }); store.dispatch(createUserChangedUrlAction('/trusted_apps')); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts index 48b2d7113f38e..4508e25d3db33 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts @@ -95,7 +95,7 @@ const refreshListIfNeeded = async ( store.dispatch( createTrustedAppsListResourceStateChangedAction({ type: 'FailedResourceState', - error, + error: error.body, lastLoadedState: getLastLoadedListResourceState(store.getState()), }) ); @@ -103,13 +103,6 @@ const refreshListIfNeeded = async ( } }; -const createTrustedAppDeletionSubmissionResourceStateChanged = ( - newState: Immutable -): Immutable => ({ - type: 'trustedAppDeletionSubmissionResourceStateChanged', - payload: { newState }, -}); - const updateCreationDialogIfNeeded = ( store: ImmutableMiddlewareAPI ) => { @@ -167,7 +160,7 @@ const submitCreationIfNeeded = async ( store.dispatch( createTrustedAppCreationSubmissionResourceStateChanged({ type: 'FailedResourceState', - error, + error: error.body, lastLoadedState: getLastLoadedResourceState(submissionResourceState), }) ); @@ -175,6 +168,13 @@ const submitCreationIfNeeded = async ( } }; +const createTrustedAppDeletionSubmissionResourceStateChanged = ( + newState: Immutable +): Immutable => ({ + type: 'trustedAppDeletionSubmissionResourceStateChanged', + payload: { newState }, +}); + const submitDeletionIfNeeded = async ( store: ImmutableMiddlewareAPI, trustedAppsService: TrustedAppsService @@ -209,7 +209,7 @@ const submitDeletionIfNeeded = async ( store.dispatch( createTrustedAppDeletionSubmissionResourceStateChanged({ type: 'FailedResourceState', - error, + error: error.body, lastLoadedState: getLastLoadedResourceState(submissionResourceState), }) ); diff --git a/x-pack/plugins/security_solution/public/management/store/middleware.ts b/x-pack/plugins/security_solution/public/management/store/middleware.ts index 77d02262e93b7..c984525edcc13 100644 --- a/x-pack/plugins/security_solution/public/management/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/management/store/middleware.ts @@ -13,10 +13,8 @@ import { MANAGEMENT_STORE_ENDPOINTS_NAMESPACE, MANAGEMENT_STORE_GLOBAL_NAMESPACE, MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE, - MANAGEMENT_STORE_POLICY_LIST_NAMESPACE, MANAGEMENT_STORE_TRUSTED_APPS_NAMESPACE, } from '../common/constants'; -import { policyListMiddlewareFactory } from '../pages/policy/store/policy_list'; import { policyDetailsMiddlewareFactory } from '../pages/policy/store/policy_details'; import { endpointMiddlewareFactory } from '../pages/endpoint_hosts/store/middleware'; import { trustedAppsPageMiddlewareFactory } from '../pages/trusted_apps/store/middleware'; @@ -31,10 +29,6 @@ export const managementMiddlewareFactory: SecuritySubPluginMiddlewareFactory = ( depsStart ) => { return [ - substateMiddlewareFactory( - createSubStateSelector(MANAGEMENT_STORE_POLICY_LIST_NAMESPACE), - policyListMiddlewareFactory(coreStart, depsStart) - ), substateMiddlewareFactory( createSubStateSelector(MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE), policyDetailsMiddlewareFactory(coreStart, depsStart) diff --git a/x-pack/plugins/security_solution/public/management/store/reducer.ts b/x-pack/plugins/security_solution/public/management/store/reducer.ts index 93186fdb67349..61cf242f21960 100644 --- a/x-pack/plugins/security_solution/public/management/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/management/store/reducer.ts @@ -9,14 +9,9 @@ import { policyDetailsReducer, initialPolicyDetailsState, } from '../pages/policy/store/policy_details/reducer'; -import { - policyListReducer, - initialPolicyListState, -} from '../pages/policy/store/policy_list/reducer'; import { MANAGEMENT_STORE_ENDPOINTS_NAMESPACE, MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE, - MANAGEMENT_STORE_POLICY_LIST_NAMESPACE, MANAGEMENT_STORE_TRUSTED_APPS_NAMESPACE, } from '../common/constants'; import { ImmutableCombineReducers } from '../../common/store'; @@ -35,7 +30,6 @@ const immutableCombineReducers: ImmutableCombineReducers = combineReducers; * Returns the initial state of the store for the SIEM Management section */ export const mockManagementState: Immutable = { - [MANAGEMENT_STORE_POLICY_LIST_NAMESPACE]: initialPolicyListState(), [MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE]: initialPolicyDetailsState(), [MANAGEMENT_STORE_ENDPOINTS_NAMESPACE]: initialEndpointListState, [MANAGEMENT_STORE_TRUSTED_APPS_NAMESPACE]: initialTrustedAppsPageState(), @@ -45,7 +39,6 @@ export const mockManagementState: Immutable = { * Redux store reducer for the SIEM Management section */ export const managementReducer = immutableCombineReducers({ - [MANAGEMENT_STORE_POLICY_LIST_NAMESPACE]: policyListReducer, [MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE]: policyDetailsReducer, [MANAGEMENT_STORE_ENDPOINTS_NAMESPACE]: endpointListReducer, [MANAGEMENT_STORE_TRUSTED_APPS_NAMESPACE]: trustedAppsPageReducer, diff --git a/x-pack/plugins/security_solution/public/management/types.ts b/x-pack/plugins/security_solution/public/management/types.ts index 8b53f4c1d8525..3feb8d56766b7 100644 --- a/x-pack/plugins/security_solution/public/management/types.ts +++ b/x-pack/plugins/security_solution/public/management/types.ts @@ -6,9 +6,9 @@ import { CombinedState } from 'redux'; import { SecurityPageName } from '../app/types'; -import { PolicyListState, PolicyDetailsState } from './pages/policy/types'; +import { PolicyDetailsState } from './pages/policy/types'; import { EndpointState } from './pages/endpoint_hosts/types'; -import { TrustedAppsListPageState } from './pages/trusted_apps/state/trusted_apps_list_page_state'; +import { TrustedAppsListPageState } from './pages/trusted_apps/state'; /** * The type for the management store global namespace. Used mostly internally to reference @@ -17,7 +17,6 @@ import { TrustedAppsListPageState } from './pages/trusted_apps/state/trusted_app export type ManagementStoreGlobalNamespace = 'management'; export type ManagementState = CombinedState<{ - policyList: PolicyListState; policyDetails: PolicyDetailsState; endpoints: EndpointState; trustedApps: TrustedAppsListPageState; diff --git a/x-pack/plugins/security_solution/public/resolver/documentation/loading_node_data.md b/x-pack/plugins/security_solution/public/resolver/documentation/loading_node_data.md new file mode 100644 index 0000000000000..011b65263be1f --- /dev/null +++ b/x-pack/plugins/security_solution/public/resolver/documentation/loading_node_data.md @@ -0,0 +1,57 @@ +# Loading Node Data + +## Introduction + +### Lifecycle Events and Node Data + +A lifecycle event is a document sent by the elastic endpoint that has `event.category` set to start, end, exec, or info. +Lifecycle events indicate the lifecycle of a process. The lifecycle events mainly pertain to endpoint data graphed +within resolver. Resolver uses end events to mark nodes as terminated. + +The resolve code base and this README uses the term _node data_. Node data for endpoint events refers to the lifecycle +events for a particular node in a resolver graph. As resolver becomes more generic in what it is able to graph, node +data will refer to whatever information helps determine what visual state a particular node in the graph should have. + +### Tree API + +The resolver backend `{id}/tree` API is tied to an `process.entity_id` and attempts to return all the lifecycle events +for each node in the returned graph. This posed a problem if a particular node in the graph had an unusually large +amount of lifecycle events. If this occurred it would mean that the request would hit its limit for the number of total +documents (10K) potentially on the first node in the graph and instead of displaying multiple nodes in the graph, it +would only display a single node. + +To get around this issue, the new `/tree` API that does not rely on the field `process.entity_id` only returns a single +document per node in the graph. This avoids means we could theoretically retrieve a graph with 10k nodes without +running into limit with ES. This adds a complication in that the UI must now request the _rest_ of the lifecycle +events for the nodes in the graph. + +## Loading Additional Lifecycle Events + +For resolver to mark whether a node has terminated, it needs the lifecycle events that would include an _end_ event +indicating the termination. Since the new `/tree` API only returns a single lifecycle event per node, resolver needs +to request the rest of the lifecycle events. Another approach could have been to have the `/tree` API return a +termination event as the single event if one existed. The advantage of having resolver request all of the lifecycle +events is that it can give the user additional insight into the events as the user is analyzing the graph. + +The backend provides an `/events` API that takes a free form `filter` parameter to narrow the request for retrieving +additional lifecycle events. This API allows lifecycle events to be retrieved for multiple nodes in the graph at the +same time. + +Resolver makes requests to the `/events` API for the node data of the nodes in view based on the current view box +of the camera. When a user moves the camera (panning, camera controls, zooming, etc) and new nodes come into view, +they initial are shown with a loading state if we have not seen those nodes in the view previously. The loading state is +to indicate that we have made a request for the their node data (i.e. lifecycle events). Once the data is returned +from the `/events` API, the nodes are updated with whether they are terminated or running. + +## Node States + +These are the different states a node can be in: + +- _**Loading**_ - The lifecycle events have been requested and we're waiting for the server to respond +- _**Error**_ - An error occurred after we requested the node's lifecycle events +- _**Running**_ - We received the node's lifecycle information and we did not see a termination event, so the process + must still be running +- _**Terminated**_ - We received the node's lifecycle information and we found a termination event + +The state is stored in a map so it can be quickly referenced. The map uses the node's ID as the key and maps to an +object containing the lifecycle events and whether the node has terminated. diff --git a/x-pack/plugins/security_solution/public/resolver/documentation/schema.md b/x-pack/plugins/security_solution/public/resolver/documentation/schema.md new file mode 100644 index 0000000000000..3870face0d821 --- /dev/null +++ b/x-pack/plugins/security_solution/public/resolver/documentation/schema.md @@ -0,0 +1,73 @@ +# Resolver Schemas + +## Introduction + +In an effort to make resolver able to graph more data sources, we found a need for a _**schema**_ to describe how +resolver should approach graphing a data source. At its basic level a schema describes how the graph should be displayed. +Specifically how to find nodes in the graph (what field to use as the ID) and how to draw lines between nodes in the graph +(the edges). + +As of 7.11 we do not currently allow the user to graph their own data. At this time we provide support (i.e. schemas) +for the elastic endpoint and winlogbeat's sysmon data. + +## Schema Format + +The predefined schemas are located here + +```typescript +const supportedSchemas: SupportedSchema[] = [ + { + name: 'endpoint', + constraints: [ + { + field: 'agent.type', + value: 'endpoint', + }, + ], + schema: { + id: 'process.entity_id', + parent: 'process.parent.entity_id', + ancestry: 'process.Ext.ancestry', + name: 'process.name', + }, + }, + { + name: 'winlogbeat', + constraints: [ + { + field: 'agent.type', + value: 'winlogbeat', + }, + { + field: 'event.module', + value: 'sysmon', + }, + ], + schema: { + id: 'process.entity_id', + parent: 'process.parent.entity_id', + name: 'process.name', + }, + }, +]; +``` + +## Schema Flow + +When a user clicks on the _analyze event_ icon in the Security Solution app, a request is made to `/entity` with the +`_id` field from the document (the internal Elasticsearch ID for the document). The `/entity` API grabs the +document from Elasticsearch and determines which predefined schema profile matches the document. To determine which +profile matches, the API uses the `constraints` array in the schema and tests that the document has the `value` for +each of the `field`. + +For the elastic endpoint schema, the constraints specifies that the document must have `agent.type` equal to `endpoint` +to match the schema. For winlogbeat, the constraints specifies that the document must have `agent.type` equal to +`winlogbeat` and `event.module` equal to `sysmon`. The API also requires that the document have the `schema.id` field +present and that it not be set to an empty string. For both the elastic endpoint and winlogbeat this is the field +`process.entity_id`. + +If the `/entity` API finds a matching schema, then it returns the `schema` portion of the above `supportedSchemas` +variable. + +The frontend then passes parts of the schema to additional backend API calls (e.g the new `/tree` API). This allows +the backend to construct the queries correctly to return a tree that adheres to the data source's schema. diff --git a/x-pack/plugins/security_solution/public/resolver/models/resolver_tree.ts b/x-pack/plugins/security_solution/public/resolver/models/resolver_tree.ts index 901e19debc991..21034edc59307 100644 --- a/x-pack/plugins/security_solution/public/resolver/models/resolver_tree.ts +++ b/x-pack/plugins/security_solution/public/resolver/models/resolver_tree.ts @@ -5,10 +5,6 @@ */ import { - ResolverTree, - ResolverNodeStats, - ResolverLifecycleNode, - SafeResolverEvent, NewResolverTree, ResolverNode, EventStats, @@ -84,51 +80,6 @@ export function nodeStats(tree: NewResolverTree): Map { - const nodeRelatedEventStats: Map = new Map(); - for (const node of lifecycleNodes(tree)) { - if (node.stats) { - nodeRelatedEventStats.set(node.entityID, node.stats); - } - } - return nodeRelatedEventStats; -} - /** * ResolverTree type is returned by the server. It organizes events into a complex structure. The * organization of events in the tree is done to associate metadata with the events. The client does not diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/breadcrumbs.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/breadcrumbs.tsx index bcb750893eaa6..98c81f08b8154 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/breadcrumbs.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/breadcrumbs.tsx @@ -11,6 +11,7 @@ import { EuiBreadcrumb, EuiBetaBadge } from '@elastic/eui'; import React, { memo, useMemo } from 'react'; import { BetaHeader, ThemedBreadcrumbs } from './styles'; import { useColors } from '../use_colors'; +import { GeneratedText } from '../generated_text'; /** * Breadcrumb menu @@ -20,6 +21,7 @@ export const Breadcrumbs = memo(function ({ breadcrumbs }: { breadcrumbs: EuiBre const crumbsWithLastSubject: EuiBreadcrumb[] = useMemo(() => { const lastcrumb = breadcrumbs.slice(-1).map((crumb) => { crumb['data-test-subj'] = 'resolver:breadcrumbs:last'; + crumb.text = {crumb.text}; return crumb; }); return [...breadcrumbs.slice(0, -1), ...lastcrumb]; diff --git a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/header.tsx b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/header.tsx index 8ab099ab78ee9..6fd24ebca7c82 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/header.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/header.tsx @@ -156,6 +156,7 @@ export const Header = React.memo( onChange={onSearchInputChange} placeholder={i18n.FILTER_PLACEHOLDER} value={searchInput} + fullWidth /> diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/bottom_bar/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/bottom_bar/index.test.tsx index c9bd1ee13cd95..f9bb8b58634fa 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/bottom_bar/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/bottom_bar/index.test.tsx @@ -8,7 +8,7 @@ import { mount } from 'enzyme'; import React from 'react'; import { TestProviders } from '../../../../common/mock/test_providers'; -import { TimelineTabs } from '../../../store/timeline/model'; +import { TimelineTabs } from '../../../../../common/types/timeline'; import { FlyoutBottomBar } from '.'; describe('FlyoutBottomBar', () => { diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/bottom_bar/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/bottom_bar/index.tsx index e6de34f1bf7a4..edc571528e94a 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/bottom_bar/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/bottom_bar/index.tsx @@ -14,7 +14,7 @@ import { DataProvider } from '../../timeline/data_providers/data_provider'; import { flattenIntoAndGroups } from '../../timeline/data_providers/helpers'; import { DataProviders } from '../../timeline/data_providers'; import { FlyoutHeaderPanel } from '../header'; -import { TimelineTabs } from '../../../store/timeline/model'; +import { TimelineTabs } from '../../../../../common/types/timeline'; export const FLYOUT_BUTTON_CLASS_NAME = 'timeline-flyout-button'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx index e22a6616ecfc6..73c2eae1402c0 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx @@ -20,7 +20,7 @@ import styled from 'styled-components'; import { FormattedRelative } from '@kbn/i18n/react'; import { useDeepEqualSelector } from '../../../../common/hooks/use_selector'; -import { TimelineStatus, TimelineType } from '../../../../../common/types/timeline'; +import { TimelineStatus, TimelineTabs, TimelineType } from '../../../../../common/types/timeline'; import { timelineActions, timelineSelectors } from '../../../store/timeline'; import { timelineDefaults } from '../../../../timelines/store/timeline/defaults'; import { AddToFavoritesButton } from '../../timeline/properties/helpers'; @@ -33,7 +33,6 @@ import { ActiveTimelines } from './active_timelines'; import * as i18n from './translations'; import * as commonI18n from '../../timeline/properties/translations'; import { getTimelineStatusByIdSelector } from './selectors'; -import { TimelineTabs } from '../../../store/timeline/model'; // to hide side borders const StyledPanel = styled(EuiPanel)` diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/index.tsx index 622efefc6230a..6881ad3ee4bc1 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/index.tsx @@ -11,10 +11,9 @@ import { useDispatch } from 'react-redux'; import styled from 'styled-components'; import { AppLeaveHandler } from '../../../../../../../src/core/public'; -import { TimelineId, TimelineStatus } from '../../../../common/types/timeline'; +import { TimelineId, TimelineStatus, TimelineTabs } from '../../../../common/types/timeline'; import { useDeepEqualSelector } from '../../../common/hooks/use_selector'; import { timelineActions } from '../../store/timeline'; -import { TimelineTabs } from '../../store/timeline/model'; import { FlyoutBottomBar } from './bottom_bar'; import { Pane } from './pane'; import { getTimelineShowStatusByIdSelector } from './selectors'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/selectors.ts b/x-pack/plugins/security_solution/public/timelines/components/flyout/selectors.ts index 0ec4fecedfa7f..e6892c121ed44 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/selectors.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/selectors.ts @@ -6,9 +6,8 @@ import { createSelector } from 'reselect'; -import { TimelineStatus } from '../../../../common/types/timeline'; +import { TimelineStatus, TimelineTabs } from '../../../../common/types/timeline'; import { timelineSelectors } from '../../store/timeline'; -import { TimelineTabs } from '../../store/timeline/model'; export const getTimelineShowStatusByIdSelector = () => createSelector(timelineSelectors.selectTimeline, (timeline) => ({ diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts index 61b0c004dcb9d..da6eec968d11c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts @@ -39,12 +39,16 @@ import { KueryFilterQueryKind } from '../../../common/store/model'; import { Note } from '../../../common/lib/note'; import moment from 'moment'; import sinon from 'sinon'; -import { TimelineId, TimelineType, TimelineStatus } from '../../../../common/types/timeline'; +import { + TimelineId, + TimelineType, + TimelineStatus, + TimelineTabs, +} from '../../../../common/types/timeline'; import { mockTimeline as mockSelectedTimeline, mockTemplate as mockSelectedTemplate, } from './__mocks__'; -import { TimelineTabs } from '../../store/timeline/model'; jest.mock('../../../common/store/inputs/actions'); jest.mock('../../../common/components/url_state/normalize_time_range.ts'); diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts index 37de75fd736af..c7821df347311 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts @@ -27,6 +27,7 @@ import { TimelineId, TimelineStatus, TimelineType, + TimelineTabs, } from '../../../../common/types/timeline'; import { @@ -42,11 +43,7 @@ import { addTimeline as dispatchAddTimeline, addNote as dispatchAddGlobalTimelineNote, } from '../../../timelines/store/timeline/actions'; -import { - ColumnHeaderOptions, - TimelineModel, - TimelineTabs, -} from '../../../timelines/store/timeline/model'; +import { ColumnHeaderOptions, TimelineModel } from '../../../timelines/store/timeline/model'; import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; import { diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/note_previews/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/note_previews/index.tsx index fc05e61442e83..d35a5f487ed8e 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/note_previews/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/note_previews/index.tsx @@ -17,6 +17,7 @@ import { MarkdownRenderer } from '../../../../common/components/markdown_editor' import { timelineActions } from '../../../store/timeline'; import { NOTE_CONTENT_CLASS_NAME } from '../../timeline/body/helpers'; import * as i18n from './translations'; +import { TimelineTabs } from '../../../../../common/types/timeline'; export const NotePreviewsContainer = styled.section` padding-top: ${({ theme }) => `${theme.eui.euiSizeS}`}; @@ -37,6 +38,7 @@ const ToggleEventDetailsButtonComponent: React.FC const handleClick = useCallback(() => { dispatch( timelineActions.toggleExpandedEvent({ + tabType: TimelineTabs.notes, timelineId, event: { eventId, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/__snapshots__/index.test.tsx.snap index d112a665d77c0..8f514ca49e848 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/__snapshots__/index.test.tsx.snap @@ -44,7 +44,6 @@ exports[`Columns it renders the expected columns 1`] = ` truncate={true} /> - 0 - 0 - 0 - 0 - 0 - 0 - 0 `; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/index.tsx index c497d4f459f00..21ca30658f530 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/index.tsx @@ -11,7 +11,8 @@ import { getOr } from 'lodash/fp'; import { DRAGGABLE_KEYBOARD_WRAPPER_CLASS_NAME } from '../../../../../common/components/drag_and_drop/helpers'; import { Ecs } from '../../../../../../common/ecs'; import { TimelineNonEcsData } from '../../../../../../common/search_strategy/timeline'; -import { ColumnHeaderOptions, TimelineTabs } from '../../../../../timelines/store/timeline/model'; +import { TimelineTabs } from '../../../../../../common/types/timeline'; +import { ColumnHeaderOptions } from '../../../../../timelines/store/timeline/model'; import { ARIA_COLUMN_INDEX_OFFSET } from '../../helpers'; import { EventsTd, EVENTS_TD_CLASS_NAME, EventsTdContent, EventsTdGroupData } from '../../styles'; import { ColumnRenderer } from '../renderers/column_renderer'; @@ -117,17 +118,17 @@ export const DataDrivenColumns = React.memo( })} - {hasRowRenderers && ( + {hasRowRenderers ? (

    {i18n.EVENT_HAS_AN_EVENT_RENDERER(ariaRowindex)}

    - )} + ) : null} - {notesCount && ( + {notesCount ? (

    {i18n.EVENT_HAS_NOTES({ row: ariaRowindex, notesCount })}

    - )} + ) : null} ))} diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.test.tsx index 0525767e616be..cff3d2890d85a 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.test.tsx @@ -11,7 +11,7 @@ import { DEFAULT_ACTIONS_COLUMN_WIDTH } from '../constants'; import * as i18n from '../translations'; import { EventColumnView } from './event_column_view'; -import { TimelineType } from '../../../../../../common/types/timeline'; +import { TimelineTabs, TimelineType } from '../../../../../../common/types/timeline'; import { useShallowEqualSelector } from '../../../../../common/hooks/use_selector'; jest.mock('../../../../../common/hooks/use_selector'); @@ -48,6 +48,7 @@ describe('EventColumnView', () => { selectedEventIds: {}, showCheckboxes: false, showNotes: false, + tabType: TimelineTabs.query, timelineId: 'timeline-test', toggleShowNotes: jest.fn(), updateNote: jest.fn(), diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx index ae8d2a47c7dc7..4e61fb7346c5c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx @@ -9,7 +9,7 @@ import React, { useCallback, useMemo } from 'react'; import { useShallowEqualSelector } from '../../../../../common/hooks/use_selector'; import { Ecs } from '../../../../../../common/ecs'; import { TimelineNonEcsData } from '../../../../../../common/search_strategy/timeline'; -import { ColumnHeaderOptions, TimelineTabs } from '../../../../../timelines/store/timeline/model'; +import { ColumnHeaderOptions } from '../../../../../timelines/store/timeline/model'; import { OnPinEvent, OnRowSelected, OnUnPinEvent } from '../../events'; import { EventsTrData } from '../../styles'; import { Actions } from '../actions'; @@ -26,7 +26,7 @@ import { InvestigateInTimelineAction } from '../../../../../detections/component import { AddEventNoteAction } from '../actions/add_note_icon_item'; import { PinEventAction } from '../actions/pin_event_action'; import { inputsModel } from '../../../../../common/store'; -import { TimelineId } from '../../../../../../common/types/timeline'; +import { TimelineId, TimelineTabs } from '../../../../../../common/types/timeline'; import { timelineSelectors } from '../../../../store/timeline'; import { timelineDefaults } from '../../../../store/timeline/defaults'; import { AddToCaseAction } from '../../../../../cases/components/timeline_actions/add_to_case_action'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/index.tsx index 92ae01b185f7a..dba08823b87fe 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/index.tsx @@ -12,7 +12,8 @@ import { TimelineItem, TimelineNonEcsData, } from '../../../../../../common/search_strategy/timeline'; -import { ColumnHeaderOptions, TimelineTabs } from '../../../../../timelines/store/timeline/model'; +import { TimelineTabs } from '../../../../../../common/types/timeline'; +import { ColumnHeaderOptions } from '../../../../../timelines/store/timeline/model'; import { OnRowSelected } from '../../events'; import { EventsTbody } from '../../styles'; import { ColumnRenderer } from '../renderers/column_renderer'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx index e3f5a744e8b7d..f00b86ef96567 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx @@ -8,13 +8,13 @@ import React, { useCallback, useMemo, useRef, useState } from 'react'; import { useDispatch } from 'react-redux'; import { useDeepEqualSelector } from '../../../../../common/hooks/use_selector'; -import { TimelineId } from '../../../../../../common/types/timeline'; +import { TimelineId, TimelineTabs } from '../../../../../../common/types/timeline'; import { BrowserFields } from '../../../../../common/containers/source'; import { TimelineItem, TimelineNonEcsData, } from '../../../../../../common/search_strategy/timeline'; -import { ColumnHeaderOptions, TimelineTabs } from '../../../../../timelines/store/timeline/model'; +import { ColumnHeaderOptions } from '../../../../../timelines/store/timeline/model'; import { OnPinEvent, OnRowSelected } from '../../events'; import { STATEFUL_EVENT_CSS_CLASS_NAME } from '../../helpers'; import { EventsTrGroup, EventsTrSupplement, EventsTrSupplementContainer } from '../../styles'; @@ -92,7 +92,10 @@ const StatefulEventComponent: React.FC = ({ const [showNotes, setShowNotes] = useState<{ [eventId: string]: boolean }>({}); const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); const expandedEvent = useDeepEqualSelector( - (state) => (getTimeline(state, timelineId) ?? timelineDefaults).expandedEvent + (state) => + (getTimeline(state, timelineId) ?? timelineDefaults).expandedEvent[ + tabType ?? TimelineTabs.query + ] ?? {} ); const getNotesByIds = useMemo(() => appSelectors.notesByIdsSelector(), []); const notesById = useDeepEqualSelector(getNotesByIds); @@ -153,6 +156,7 @@ const StatefulEventComponent: React.FC = ({ dispatch( timelineActions.toggleExpandedEvent({ + tabType, timelineId, event: { eventId, @@ -161,10 +165,10 @@ const StatefulEventComponent: React.FC = ({ }) ); - if (timelineId === TimelineId.active) { + if (timelineId === TimelineId.active && tabType === TimelineTabs.query) { activeTimeline.toggleExpandedEvent({ eventId, indexName }); } - }, [dispatch, event._id, event._index, timelineId]); + }, [dispatch, event._id, event._index, tabType, timelineId]); const associateNote = useCallback( (noteId: string) => { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/helpers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/helpers.tsx index 0295d44b646d7..3a738db981b38 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/helpers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/helpers.tsx @@ -16,12 +16,11 @@ import { TimelineTypeLiteral, TimelineType, TimelineId, + TimelineTabs, } from '../../../../../common/types/timeline'; import { OnPinEvent, OnUnPinEvent } from '../events'; import { ActionIconItem } from './actions/action_icon_item'; - import * as i18n from './translations'; -import { TimelineTabs } from '../../../store/timeline/model'; // eslint-disable-next-line @typescript-eslint/no-explicit-any export const omitTypenameAndEmpty = (k: string, v: any): any | undefined => diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx index c8e911db85f64..cc04b83382998 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx @@ -17,7 +17,7 @@ import { BodyComponent, StatefulBodyProps } from '.'; import { Sort } from './sort'; import { useMountAppended } from '../../../../common/utils/use_mount_appended'; import { timelineActions } from '../../../store/timeline'; -import { TimelineTabs } from '../../../store/timeline/model'; +import { TimelineTabs } from '../../../../../common/types/timeline'; const mockSort: Sort[] = [ { @@ -221,4 +221,78 @@ describe('Body', () => { ); }); }); + + describe('event details', () => { + beforeEach(() => { + mockDispatch.mockReset(); + }); + test('call the right reduce action to show event details for query tab', async () => { + const wrapper = mount( + + + + ); + + wrapper.find(`[data-test-subj="expand-event"]`).first().simulate('click'); + wrapper.update(); + expect(mockDispatch).toBeCalledTimes(1); + expect(mockDispatch.mock.calls[0][0]).toEqual({ + payload: { + event: { + eventId: '1', + indexName: undefined, + }, + tabType: 'query', + timelineId: 'timeline-test', + }, + type: 'x-pack/security_solution/local/timeline/TOGGLE_EXPANDED_EVENT', + }); + }); + + test('call the right reduce action to show event details for pinned tab', async () => { + const wrapper = mount( + + + + ); + + wrapper.find(`[data-test-subj="expand-event"]`).first().simulate('click'); + wrapper.update(); + expect(mockDispatch).toBeCalledTimes(1); + expect(mockDispatch.mock.calls[0][0]).toEqual({ + payload: { + event: { + eventId: '1', + indexName: undefined, + }, + tabType: 'pinned', + timelineId: 'timeline-test', + }, + type: 'x-pack/security_solution/local/timeline/TOGGLE_EXPANDED_EVENT', + }); + }); + + test('call the right reduce action to show event details for notes tab', async () => { + const wrapper = mount( + + + + ); + + wrapper.find(`[data-test-subj="expand-event"]`).first().simulate('click'); + wrapper.update(); + expect(mockDispatch).toBeCalledTimes(1); + expect(mockDispatch.mock.calls[0][0]).toEqual({ + payload: { + event: { + eventId: '1', + indexName: undefined, + }, + tabType: 'notes', + timelineId: 'timeline-test', + }, + type: 'x-pack/security_solution/local/timeline/TOGGLE_EXPANDED_EVENT', + }); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.tsx index 4a33d0d3af33e..a03f4c07645ad 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.tsx @@ -10,7 +10,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { connect, ConnectedProps } from 'react-redux'; import deepEqual from 'fast-deep-equal'; -import { RowRendererId, TimelineId } from '../../../../../common/types/timeline'; +import { RowRendererId, TimelineId, TimelineTabs } from '../../../../../common/types/timeline'; import { FIRST_ARIA_INDEX, ARIA_COLINDEX_ATTRIBUTE, @@ -21,7 +21,7 @@ import { BrowserFields } from '../../../../common/containers/source'; import { TimelineItem } from '../../../../../common/search_strategy/timeline'; import { inputsModel, State } from '../../../../common/store'; import { useManageTimeline } from '../../manage_timeline'; -import { ColumnHeaderOptions, TimelineModel, TimelineTabs } from '../../../store/timeline/model'; +import { ColumnHeaderOptions, TimelineModel } from '../../../store/timeline/model'; import { timelineDefaults } from '../../../store/timeline/defaults'; import { timelineActions, timelineSelectors } from '../../../store/timeline'; import { OnRowSelected, OnSelectAll } from '../events'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/event_details.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/event_details.tsx index 9895f4eda0e6c..c75f8a0d1c170 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/event_details.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/event_details.tsx @@ -25,10 +25,12 @@ import { useDeepEqualSelector } from '../../../common/hooks/use_selector'; import { useTimelineEventsDetails } from '../../containers/details'; import { timelineSelectors } from '../../store/timeline'; import { timelineDefaults } from '../../store/timeline/defaults'; +import { TimelineTabs } from '../../../../common/types/timeline'; interface EventDetailsProps { browserFields: BrowserFields; docValueFields: DocValueFields[]; + tabType: TimelineTabs; timelineId: string; handleOnEventClosed?: HandleOnEventClosed; } @@ -36,12 +38,13 @@ interface EventDetailsProps { const EventDetailsComponent: React.FC = ({ browserFields, docValueFields, + tabType, timelineId, handleOnEventClosed, }) => { const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); const expandedEvent = useDeepEqualSelector( - (state) => (getTimeline(state, timelineId) ?? timelineDefaults).expandedEvent + (state) => (getTimeline(state, timelineId) ?? timelineDefaults).expandedEvent[tabType] ?? {} ); const [loading, detailsData] = useTimelineEventsDetails({ @@ -71,6 +74,7 @@ const EventDetailsComponent: React.FC = ({ isAlert={isAlert} loading={loading} timelineId={timelineId} + timelineTabType={tabType} /> ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/expandable_event/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/expandable_event/index.tsx index df8e84b4e2a78..a38fde0e3f548 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/expandable_event/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/expandable_event/index.tsx @@ -20,7 +20,7 @@ import { import React, { useMemo, useState } from 'react'; import styled from 'styled-components'; -import { TimelineExpandedEvent } from '../../../../../common/types/timeline'; +import { TimelineExpandedEventType, TimelineTabs } from '../../../../../common/types/timeline'; import { BrowserFields } from '../../../../common/containers/source'; import { EventDetails, @@ -35,9 +35,10 @@ export type HandleOnEventClosed = () => void; interface Props { browserFields: BrowserFields; detailsData: TimelineEventsDetailsItem[] | null; - event: TimelineExpandedEvent; + event: TimelineExpandedEventType; isAlert: boolean; loading: boolean; + timelineTabType: TimelineTabs | 'flyout'; timelineId: string; } @@ -71,7 +72,7 @@ export const ExpandableEventTitle = React.memo( ExpandableEventTitle.displayName = 'ExpandableEventTitle'; export const ExpandableEvent = React.memo( - ({ browserFields, event, timelineId, isAlert, loading, detailsData }) => { + ({ browserFields, event, timelineId, timelineTabType, isAlert, loading, detailsData }) => { const [view, setView] = useState(EventsViewType.summaryView); const message = useMemo(() => { @@ -116,6 +117,7 @@ export const ExpandableEvent = React.memo( id={event.eventId!} isAlert={isAlert} onViewSelected={setView} + timelineTabType={timelineTabType} timelineId={timelineId} view={view} /> diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx index 41ac16a12e648..2b26e3f9eb0b5 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx @@ -17,7 +17,7 @@ import { isTab } from '../../../common/components/accessibility/helpers'; import { useSourcererScope } from '../../../common/containers/sourcerer'; import { SourcererScopeName } from '../../../common/store/sourcerer/model'; import { FlyoutHeader, FlyoutHeaderPanel } from '../flyout/header'; -import { TimelineType } from '../../../../common/types/timeline'; +import { TimelineType, TimelineTabs } from '../../../../common/types/timeline'; import { useDeepEqualSelector, useShallowEqualSelector } from '../../../common/hooks/use_selector'; import { activeTimeline } from '../../containers/active_timeline_context'; import { EVENTS_COUNT_BUTTON_CLASS_NAME, onTimelineTabKeyPressed } from './helpers'; @@ -68,7 +68,9 @@ const StatefulTimelineComponent: React.FC = ({ timelineId }) => { id: timelineId, columns: defaultHeaders, indexNames: selectedPatterns, - expandedEvent: activeTimeline.getExpandedEvent(), + expandedEvent: { + [TimelineTabs.query]: activeTimeline.getExpandedEvent(), + }, show: false, }) ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/notes_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/notes_tab_content/index.tsx index bfb990cbd7364..34e5aed885d5c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/notes_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/notes_tab_content/index.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { filter, pick, uniqBy } from 'lodash/fp'; +import { filter, uniqBy } from 'lodash/fp'; import { EuiAvatar, EuiFlexGroup, @@ -21,17 +21,17 @@ import styled from 'styled-components'; import { useSourcererScope } from '../../../../common/containers/sourcerer'; import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; -import { timelineActions, timelineSelectors } from '../../../store/timeline'; +import { timelineActions } from '../../../store/timeline'; import { useDeepEqualSelector } from '../../../../common/hooks/use_selector'; -import { TimelineStatus } from '../../../../../common/types/timeline'; +import { TimelineStatus, TimelineTabs } from '../../../../../common/types/timeline'; import { appSelectors } from '../../../../common/store/app'; -import { timelineDefaults } from '../../../store/timeline/defaults'; import { AddNote } from '../../notes/add_note'; import { CREATED_BY, NOTES } from '../../notes/translations'; import { PARTICIPANTS } from '../../../../cases/translations'; import { NotePreviews } from '../../open_timeline/note_previews'; import { TimelineResultNote } from '../../open_timeline/types'; import { EventDetails } from '../event_details'; +import { getTimelineNoteSelector } from './selectors'; const FullWidthFlexGroup = styled(EuiFlexGroup)` width: 100%; @@ -121,18 +121,14 @@ interface NotesTabContentProps { const NotesTabContentComponent: React.FC = ({ timelineId }) => { const dispatch = useDispatch(); - const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); + const getTimelineNotes = useMemo(() => getTimelineNoteSelector(), []); const { createdBy, expandedEvent, eventIdToNoteIds, + noteIds, status: timelineStatus, - } = useDeepEqualSelector((state) => - pick( - ['createdBy', 'expandedEvent', 'eventIdToNoteIds', 'status'], - getTimeline(state, timelineId) ?? timelineDefaults - ) - ); + } = useDeepEqualSelector((state) => getTimelineNotes(state, timelineId)); const { browserFields, docValueFields } = useSourcererScope(SourcererScopeName.timeline); @@ -142,7 +138,20 @@ const NotesTabContentComponent: React.FC = ({ timelineId } ); const [newNote, setNewNote] = useState(''); const isImmutable = timelineStatus === TimelineStatus.immutable; - const notes: TimelineResultNote[] = useDeepEqualSelector(getNotesAsCommentsList); + const appNotes: TimelineResultNote[] = useDeepEqualSelector(getNotesAsCommentsList); + + const allTimelineNoteIds = useMemo(() => { + const eventNoteIds = Object.values(eventIdToNoteIds).reduce( + (acc, v) => [...acc, ...v], + [] + ); + return [...noteIds, ...eventNoteIds]; + }, [noteIds, eventIdToNoteIds]); + + const notes = useMemo( + () => appNotes.filter((appNote) => allTimelineNoteIds.includes(appNote?.noteId ?? '-1')), + [appNotes, allTimelineNoteIds] + ); // filter for savedObjectId to make sure we don't display `elastic` user while saving the note const participants = useMemo(() => uniqBy('updatedBy', filter('savedObjectId', notes)), [notes]); @@ -153,20 +162,21 @@ const NotesTabContentComponent: React.FC = ({ timelineId } ); const handleOnEventClosed = useCallback(() => { - dispatch(timelineActions.toggleExpandedEvent({ timelineId })); + dispatch(timelineActions.toggleExpandedEvent({ tabType: TimelineTabs.notes, timelineId })); }, [dispatch, timelineId]); const EventDetailsContent = useMemo( () => - expandedEvent.eventId ? ( + expandedEvent?.eventId != null ? ( ) : null, - [browserFields, docValueFields, expandedEvent.eventId, handleOnEventClosed, timelineId] + [browserFields, docValueFields, expandedEvent, handleOnEventClosed, timelineId] ); const SidebarContent = useMemo( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/notes_tab_content/selectors.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/notes_tab_content/selectors.ts new file mode 100644 index 0000000000000..37ee980b1a4ae --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/notes_tab_content/selectors.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { createSelector } from 'reselect'; + +import { timelineSelectors } from '../../../store/timeline'; + +export const getTimelineNoteSelector = () => + createSelector(timelineSelectors.selectTimeline, (timeline) => { + return { + createdBy: timeline.createdBy, + expandedEvent: timeline.expandedEvent?.notes ?? {}, + eventIdToNoteIds: timeline?.eventIdToNoteIds ?? {}, + noteIds: timeline.noteIds, + status: timeline.status, + }; + }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/pinned_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/pinned_tab_content/index.tsx index a0d2ca57f90b3..1054b5405d9d9 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/pinned_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/pinned_tab_content/index.tsx @@ -23,11 +23,12 @@ import { EventDetailsWidthProvider } from '../../../../common/components/events_ import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; import { timelineDefaults } from '../../../store/timeline/defaults'; import { useSourcererScope } from '../../../../common/containers/sourcerer'; -import { TimelineModel, TimelineTabs } from '../../../store/timeline/model'; +import { TimelineModel } from '../../../store/timeline/model'; import { EventDetails } from '../event_details'; import { ToggleExpandedEvent } from '../../../store/timeline/actions'; import { State } from '../../../../common/store'; import { calculateTotalPages } from '../helpers'; +import { TimelineTabs } from '../../../../../common/types/timeline'; const StyledEuiFlyoutBody = styled(EuiFlyoutBody)` overflow-y: hidden; @@ -167,7 +168,7 @@ export const PinnedTabContentComponent: React.FC = ({ }); const handleOnEventClosed = useCallback(() => { - onEventClosed({ timelineId }); + onEventClosed({ tabType: TimelineTabs.pinned, timelineId }); }, [timelineId, onEventClosed]); return ( @@ -218,6 +219,7 @@ export const PinnedTabContentComponent: React.FC = ({ @@ -248,7 +250,7 @@ const makeMapStateToProps = () => { itemsPerPage, itemsPerPageOptions, pinnedEventIds, - showEventDetails: !!expandedEvent.eventId, + showEventDetails: !!expandedEvent[TimelineTabs.pinned]?.eventId, sort, }; }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.test.tsx index 4769c826a2fad..b24a4afcbeea2 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.test.tsx @@ -17,12 +17,11 @@ import { QueryTabContentComponent, Props as QueryTabContentComponentProps } from import { Sort } from '../body/sort'; import { mockDataProviders } from '../data_providers/mock/mock_data_providers'; import { useMountAppended } from '../../../../common/utils/use_mount_appended'; -import { TimelineId, TimelineStatus } from '../../../../../common/types/timeline'; +import { TimelineId, TimelineStatus, TimelineTabs } from '../../../../../common/types/timeline'; import { useTimelineEvents } from '../../../containers/index'; import { useTimelineEventsDetails } from '../../../containers/details/index'; import { useSourcererScope } from '../../../../common/containers/sourcerer'; import { mockSourcererScope } from '../../../../common/containers/sourcerer/mocks'; -import { TimelineTabs } from '../../../store/timeline/model'; jest.mock('../../../containers/index', () => ({ useTimelineEvents: jest.fn(), diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx index c0840d58174b3..d4c03117adcb9 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx @@ -14,7 +14,7 @@ import { EuiBadge, } from '@elastic/eui'; import { isEmpty } from 'lodash/fp'; -import React, { useState, useMemo, useEffect, useCallback, useRef } from 'react'; +import React, { useState, useMemo, useEffect, useCallback } from 'react'; import styled from 'styled-components'; import { Dispatch } from 'redux'; import { connect, ConnectedProps } from 'react-redux'; @@ -33,7 +33,7 @@ import { calculateTotalPages, combineQueries } from '../helpers'; import { TimelineRefetch } from '../refetch_timeline'; import { esQuery, FilterManager } from '../../../../../../../../src/plugins/data/public'; import { useManageTimeline } from '../../manage_timeline'; -import { TimelineEventsType, TimelineId } from '../../../../../common/types/timeline'; +import { TimelineEventsType, TimelineId, TimelineTabs } from '../../../../../common/types/timeline'; import { requiredFieldsForActions } from '../../../../detections/components/alerts_table/default_config'; import { SuperDatePicker } from '../../../../common/components/super_date_picker'; import { EventDetailsWidthProvider } from '../../../../common/components/events_viewer/event_details_width_context'; @@ -44,7 +44,7 @@ import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; import { timelineDefaults } from '../../../../timelines/store/timeline/defaults'; import { useSourcererScope } from '../../../../common/containers/sourcerer'; import { useTimelineEventsCountPortal } from '../../../../common/hooks/use_timeline_events_count'; -import { TimelineModel, TimelineTabs } from '../../../../timelines/store/timeline/model'; +import { TimelineModel } from '../../../../timelines/store/timeline/model'; import { EventDetails } from '../event_details'; import { TimelineDatePickerLock } from '../date_picker_lock'; import { HideShowContainer } from '../styles'; @@ -173,9 +173,6 @@ export const QueryTabContentComponent: React.FC = ({ kqlQueryExpression, ]); - const prevCombinedQueries = useRef<{ - filterQuery: string; - } | null>(null); const combinedQueries = useMemo( () => combineQueries({ @@ -211,12 +208,7 @@ export const QueryTabContentComponent: React.FC = ({ return [...columnFields, ...requiredFieldsForActions]; }, [columns]); - const prevTimelineQuerySortField = useRef< - Array<{ - field: string; - direction: Direction; - }> - >([]); + const timelineQuerySortField = useMemo( () => sort.map(({ columnId, sortDirection }) => ({ @@ -252,7 +244,7 @@ export const QueryTabContentComponent: React.FC = ({ }); const handleOnEventClosed = useCallback(() => { - onEventClosed({ timelineId }); + onEventClosed({ tabType: TimelineTabs.query, timelineId }); if (timelineId === TimelineId.active) { activeTimeline.toggleExpandedEvent({ @@ -266,17 +258,6 @@ export const QueryTabContentComponent: React.FC = ({ setIsTimelineLoading({ id: timelineId, isLoading: isQueryLoading || loadingSourcerer }); }, [loadingSourcerer, timelineId, isQueryLoading, setIsTimelineLoading]); - useEffect(() => { - if (!deepEqual(prevCombinedQueries.current, combinedQueries)) { - prevCombinedQueries.current = combinedQueries; - handleOnEventClosed(); - } - if (!deepEqual(prevTimelineQuerySortField.current, timelineQuerySortField)) { - prevTimelineQuerySortField.current = timelineQuerySortField; - handleOnEventClosed(); - } - }, [combinedQueries, handleOnEventClosed, timelineQuerySortField]); - return ( <> @@ -368,6 +349,7 @@ export const QueryTabContentComponent: React.FC = ({ @@ -416,7 +398,7 @@ const makeMapStateToProps = () => { dataProviders, eventType: eventType ?? 'raw', end: input.timerange.to, - expandedEvent, + expandedEvent: expandedEvent[TimelineTabs.query] ?? {}, filters: timelineFilter, timelineId, isLive: input.policy.kind === 'interval', @@ -425,7 +407,7 @@ const makeMapStateToProps = () => { kqlMode, kqlQueryExpression, showCallOutUnauthorizedMsg: getShowCallOutUnauthorizedMsg(state), - showEventDetails: !!expandedEvent.eventId, + showEventDetails: !!expandedEvent[TimelineTabs.query]?.eventId, show, sort, start: input.timerange.from, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/search_or_filter.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/search_or_filter.tsx index 1b5e6932dff08..7ae8cf4c6507c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/search_or_filter.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/search_or_filter.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiFlexGroup, EuiFlexItem, EuiSuperSelect, EuiToolTip } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; import React, { useCallback } from 'react'; import styled, { createGlobalStyle } from 'styled-components'; @@ -15,6 +15,7 @@ import { DispatchUpdateReduxTime } from '../../../../common/components/super_dat import { DataProvider } from '../data_providers/data_provider'; import { QueryBarTimeline } from '../query_bar'; +import { EuiSuperSelect } from './super_select'; import { options } from './helpers'; import * as i18n from './translations'; @@ -28,8 +29,8 @@ const SearchOrFilterGlobalStyle = createGlobalStyle` width: 350px !important; } - .${searchOrFilterPopoverClassName}__popoverPanel { - width: ${searchOrFilterPopoverWidth}; + .${searchOrFilterPopoverClassName}.euiPopover__panel { + width: ${searchOrFilterPopoverWidth} !important; .euiSuperSelect__listbox { width: ${searchOrFilterPopoverWidth} !important; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/super_select.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/super_select.tsx new file mode 100644 index 0000000000000..36a6ea7385b8a --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/super_select.tsx @@ -0,0 +1,273 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* + Duplicated EuiSuperSelect, because due to the recent changes there is no way to pass panelClassName + prop to EuiInputPopover, which doesn't allow us to properly style the EuiInputPopover panel + (we want the panel to be wider than the input) +*/ + +import { + EuiSuperSelectProps, + EuiScreenReaderOnly, + EuiSuperSelectControl, + EuiInputPopover, + EuiContextMenuItem, + keys, + EuiI18n, +} from '@elastic/eui'; +import React, { Component } from 'react'; +import classNames from 'classnames'; + +enum ShiftDirection { + BACK = 'back', + FORWARD = 'forward', +} + +export class EuiSuperSelect extends Component> { + static defaultProps = { + compressed: false, + fullWidth: false, + hasDividers: false, + isInvalid: false, + isLoading: false, + }; + + private itemNodes: Array = []; + private _isMounted: boolean = false; + + state = { + isPopoverOpen: this.props.isOpen || false, + }; + + componentDidMount() { + this._isMounted = true; + if (this.props.isOpen) { + this.openPopover(); + } + } + + componentWillUnmount() { + this._isMounted = false; + } + + setItemNode = (node: HTMLButtonElement | null, index: number) => { + this.itemNodes[index] = node; + }; + + openPopover = () => { + this.setState({ + isPopoverOpen: true, + }); + + const focusSelected = () => { + const indexOfSelected = this.props.options.reduce((acc, option, index) => { + if (acc != null) return acc; + if (option == null) return null; + return option.value === this.props.valueOfSelected ? index : null; + }, null); + + requestAnimationFrame(() => { + if (!this._isMounted) { + return; + } + + if (this.props.valueOfSelected != null) { + if (indexOfSelected != null) { + this.focusItemAt(indexOfSelected); + } else { + focusSelected(); + } + } + }); + }; + + requestAnimationFrame(focusSelected); + }; + + closePopover = () => { + this.setState({ + isPopoverOpen: false, + }); + }; + + itemClicked = (value: T) => { + this.setState({ + isPopoverOpen: false, + }); + if (this.props.onChange) { + this.props.onChange(value); + } + }; + + onSelectKeyDown = (event: React.KeyboardEvent) => { + if (event.key === keys.ARROW_UP || event.key === keys.ARROW_DOWN) { + event.preventDefault(); + event.stopPropagation(); + this.openPopover(); + } + }; + + onItemKeyDown = (event: React.KeyboardEvent) => { + switch (event.key) { + case keys.ESCAPE: + // close the popover and prevent ancestors from handling + event.preventDefault(); + event.stopPropagation(); + this.closePopover(); + break; + + case keys.TAB: + // no-op + event.preventDefault(); + event.stopPropagation(); + break; + + case keys.ARROW_UP: + event.preventDefault(); + event.stopPropagation(); + this.shiftFocus(ShiftDirection.BACK); + break; + + case keys.ARROW_DOWN: + event.preventDefault(); + event.stopPropagation(); + this.shiftFocus(ShiftDirection.FORWARD); + break; + } + }; + + focusItemAt(index: number) { + const targetElement = this.itemNodes[index]; + if (targetElement != null) { + targetElement.focus(); + } + } + + shiftFocus(direction: ShiftDirection) { + const currentIndex = this.itemNodes.indexOf(document.activeElement as HTMLButtonElement); + let targetElementIndex: number; + + if (currentIndex === -1) { + // somehow the select options has lost focus + targetElementIndex = 0; + } else { + if (direction === ShiftDirection.BACK) { + targetElementIndex = currentIndex === 0 ? this.itemNodes.length - 1 : currentIndex - 1; + } else { + targetElementIndex = currentIndex === this.itemNodes.length - 1 ? 0 : currentIndex + 1; + } + } + + this.focusItemAt(targetElementIndex); + } + + render() { + const { + className, + options, + valueOfSelected, + onChange, + isOpen, + isInvalid, + hasDividers, + itemClassName, + itemLayoutAlign, + fullWidth, + popoverClassName, + compressed, + ...rest + } = this.props; + + const popoverClasses = classNames('euiSuperSelect', popoverClassName); + + const buttonClasses = classNames( + { + // eslint-disable-next-line @typescript-eslint/naming-convention + 'euiSuperSelect--isOpen__button': this.state.isPopoverOpen, + }, + className + ); + + const itemClasses = classNames( + 'euiSuperSelect__item', + { + // eslint-disable-next-line @typescript-eslint/naming-convention + 'euiSuperSelect__item--hasDividers': hasDividers, + }, + itemClassName + ); + + const button = ( + + ); + + const items = options.map((option, index) => { + const { value, dropdownDisplay, inputDisplay, ...optionRest } = option; + + return ( + this.itemClicked(value)} + onKeyDown={this.onItemKeyDown} + layoutAlign={itemLayoutAlign} + buttonRef={(node) => this.setItemNode(node, index)} + role="option" + id={value} + aria-selected={valueOfSelected === value} + {...optionRest} + > + {dropdownDisplay || inputDisplay} + + ); + }); + + return ( + + +

    + +

    +
    +
    + {items} +
    +
    + ); + } +} diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.test.tsx index 519372d0ac797..6e31b822e73c2 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.test.tsx @@ -3,11 +3,14 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ + +import { EuiSelectableProps } from '@elastic/eui'; import React from 'react'; import { shallow, ShallowWrapper, mount } from 'enzyme'; + import { TimelineType } from '../../../../../common/types/timeline'; import { SortFieldTimeline, Direction } from '../../../../graphql/types'; -import { SelectableTimeline, ORIGINAL_PAGE_SIZE, SearchProps } from './'; +import { SelectableTimeline, ORIGINAL_PAGE_SIZE } from './'; const mockFetchAllTimeline = jest.fn(); jest.mock('../../../containers/all', () => { @@ -40,10 +43,10 @@ describe('SelectableTimeline', () => { }); test('render placeholder', () => { - const searchProps: SearchProps = wrapper + const searchProps: EuiSelectableProps['searchProps'] = wrapper .find('[data-test-subj="selectable-input"]') .prop('searchProps'); - expect(searchProps.placeholder).toEqual('e.g. Timeline name or description'); + expect(searchProps!.placeholder).toEqual('e.g. Timeline name or description'); }); }); @@ -58,10 +61,10 @@ describe('SelectableTimeline', () => { }); test('render placeholder', () => { - const searchProps: SearchProps = wrapper + const searchProps: EuiSelectableProps['searchProps'] = wrapper .find('[data-test-subj="selectable-input"]') .prop('searchProps'); - expect(searchProps.placeholder).toEqual('e.g. Timeline template name or description'); + expect(searchProps!.placeholder).toEqual('e.g. Timeline template name or description'); }); }); }); @@ -89,7 +92,7 @@ describe('SelectableTimeline', () => { jest.clearAllMocks(); }); - test('shoule be called with correct args', () => { + test('should be called with correct args', () => { expect(mockFetchAllTimeline).toBeCalledWith(args); }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.tsx index a80576c7237f4..7dbc4a17f4b3b 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.tsx @@ -12,8 +12,7 @@ import { EuiIcon, EuiTextColor, EuiSelectableOption, - EuiPortal, - EuiFilterGroup, + EuiSelectableProps, EuiFilterButton, } from '@elastic/eui'; import { isEmpty, debounce } from 'lodash/fp'; @@ -41,30 +40,9 @@ const MyEuiFlexItem = styled(EuiFlexItem)` white-space: nowrap; `; -const MyEuiFlexGroup = styled(EuiFlexGroup)` - padding 0px 4px; -`; - -const EuiSelectableContainer = styled.div<{ isLoading: boolean }>` - .euiSelectable { - .euiFormControlLayout__childrenWrapper { - display: flex; - } - ${({ isLoading }) => `${ - isLoading - ? ` - .euiFormControlLayoutIcons { - display: none; - } - .euiFormControlLayoutIcons.euiFormControlLayoutIcons--right { - display: block; - left: 12px; - top: 12px; - }` - : '' - } - `} - } +const StyledEuiFilterButton = styled(EuiFilterButton)` + border-top: 0; + border-bottom: 0; `; export const ORIGINAL_PAGE_SIZE = 50; @@ -95,15 +73,6 @@ export interface SelectableTimelineProps { timelineType: TimelineTypeLiteral; } -export interface SearchProps { - 'data-test-subj'?: string; - isLoading: boolean; - placeholder: string; - onSearch: (arg: string) => void; - incremental: boolean; - inputRef: (arg: HTMLInputElement | null) => void; -} - const SelectableTimelineComponent: React.FC = ({ hideUntitled = false, getSelectableOptions, @@ -115,7 +84,6 @@ const SelectableTimelineComponent: React.FC = ({ const [heightTrigger, setHeightTrigger] = useState(0); const [searchTimelineValue, setSearchTimelineValue] = useState(''); const [onlyFavorites, setOnlyFavorites] = useState(false); - const [searchRef, setSearchRef] = useState(null); const { fetchAllTimeline, timelines, loading, totalCount: timelineCount } = useGetAllTimeline(); const selectableListOuterRef = useRef(null); const selectableListInnerRef = useRef(null); @@ -156,8 +124,8 @@ const SelectableTimelineComponent: React.FC = ({ [heightTrigger, pageSize] ); - const renderTimelineOption = useCallback((option, searchValue) => { - return ( + const renderTimelineOption = useCallback( + (option, searchValue) => ( = ({ /> - ); - }, []); + ), + [] + ); const handleTimelineChange = useCallback( (options) => { @@ -215,39 +184,53 @@ const SelectableTimelineComponent: React.FC = ({ [onClosePopover, onTimelineChange] ); - const favoritePortal = useMemo( - () => - searchRef != null ? ( - - - - - - {i18nTimeline.ONLY_FAVORITES} - - - - - - ) : null, - [searchRef, onlyFavorites, handleOnToggleOnlyFavorites] + const EuiSelectableContent = useCallback( + (list, search) => ( + <> + {search} + {list} + + ), + [] ); - const searchProps: SearchProps = { - 'data-test-subj': 'timeline-super-select-search-box', - isLoading: loading, - placeholder: useMemo(() => i18n.SEARCH_BOX_TIMELINE_PLACEHOLDER(timelineType), [timelineType]), - onSearch: onSearchTimeline, - incremental: true, - inputRef: (node: HTMLInputElement | null) => { - setSearchRef(node); - }, - }; + const searchProps: EuiSelectableProps['searchProps'] = useMemo( + () => ({ + 'data-test-subj': 'timeline-super-select-search-box', + placeholder: i18n.SEARCH_BOX_TIMELINE_PLACEHOLDER(timelineType), + onSearch: onSearchTimeline, + incremental: true, + append: ( + + {i18nTimeline.ONLY_FAVORITES} + + ), + }), + [handleOnToggleOnlyFavorites, onSearchTimeline, onlyFavorites, timelineType] + ); + + const listProps: EuiSelectableProps['listProps'] = useMemo( + () => ({ + rowHeight: TIMELINE_ITEM_HEIGHT, + showIcons: false, + windowProps: { + onScroll: ({ scrollOffset }) => + handleOnScroll( + timelines.filter((t) => !hideUntitled || t.title !== '').length, + timelineCount, + scrollOffset + ), + outerRef: selectableListOuterRef, + innerRef: selectableListInnerRef, + }, + }), + [handleOnScroll, hideUntitled, timelineCount, timelines] + ); useEffect(() => { fetchAllTimeline({ @@ -267,46 +250,25 @@ const SelectableTimelineComponent: React.FC = ({ }, [fetchAllTimeline, onlyFavorites, pageSize, searchTimelineValue, timelineType]); return ( - - - handleOnScroll( - timelines.filter((t) => !hideUntitled || t.title !== '').length, - timelineCount, - scrollOffset - ), - outerRef: selectableListOuterRef, - innerRef: selectableListInnerRef, - }, - }} - renderOption={renderTimelineOption} - onChange={handleTimelineChange} - searchable - searchProps={searchProps} - singleSelection={true} - options={getSelectableOptions({ - timelines, - onlyFavorites, - searchTimelineValue, - timelineType, - })} - > - {(list, search) => ( - <> - {search} - {favoritePortal} - {list} - - )} - - + + {EuiSelectableContent} + ); }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx index c97571fbbd6f3..25312ac2747ae 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx @@ -8,16 +8,21 @@ import { EuiBadge, EuiLoadingContent, EuiTabs, EuiTab } from '@elastic/eui'; import React, { lazy, memo, Suspense, useCallback, useEffect, useMemo } from 'react'; import { useDispatch } from 'react-redux'; import styled from 'styled-components'; +import { TimelineTabs } from '../../../../../common/types/timeline'; -import { useShallowEqualSelector } from '../../../../common/hooks/use_selector'; +import { + useShallowEqualSelector, + useDeepEqualSelector, +} from '../../../../common/hooks/use_selector'; import { TimelineEventsCountBadge } from '../../../../common/hooks/use_timeline_events_count'; import { timelineActions } from '../../../store/timeline'; -import { TimelineTabs } from '../../../store/timeline/model'; import { getActiveTabSelector, + getNoteIdsSelector, getNotesSelector, getPinnedEventSelector, getShowTimelineSelector, + getEventIdToNoteIdsSelector, } from './selectors'; import * as i18n from './translations'; @@ -137,37 +142,55 @@ const TabsContentComponent: React.FC = ({ timelineId, graphEve const getActiveTab = useMemo(() => getActiveTabSelector(), []); const getShowTimeline = useMemo(() => getShowTimelineSelector(), []); const getNumberOfPinnedEvents = useMemo(() => getPinnedEventSelector(), []); - const getNumberOfNotes = useMemo(() => getNotesSelector(), []); + const getAppNotes = useMemo(() => getNotesSelector(), []); + const getTimelineNoteIds = useMemo(() => getNoteIdsSelector(), []); + const getTimelinePinnedEventNotes = useMemo(() => getEventIdToNoteIdsSelector(), []); + const activeTab = useShallowEqualSelector((state) => getActiveTab(state, timelineId)); const showTimeline = useShallowEqualSelector((state) => getShowTimeline(state, timelineId)); const numberOfPinnedEvents = useShallowEqualSelector((state) => getNumberOfPinnedEvents(state, timelineId) ); - const numberOfNotes = useShallowEqualSelector((state) => getNumberOfNotes(state)); + const globalTimelineNoteIds = useDeepEqualSelector((state) => + getTimelineNoteIds(state, timelineId) + ); + const eventIdToNoteIds = useDeepEqualSelector((state) => + getTimelinePinnedEventNotes(state, timelineId) + ); + const appNotes = useDeepEqualSelector((state) => getAppNotes(state)); + + const allTimelineNoteIds = useMemo(() => { + const eventNoteIds = Object.values(eventIdToNoteIds).reduce( + (acc, v) => [...acc, ...v], + [] + ); + return [...globalTimelineNoteIds, ...eventNoteIds]; + }, [globalTimelineNoteIds, eventIdToNoteIds]); + + const numberOfNotes = useMemo( + () => appNotes.filter((appNote) => allTimelineNoteIds.includes(appNote.id)).length, + [appNotes, allTimelineNoteIds] + ); const setQueryAsActiveTab = useCallback(() => { - dispatch(timelineActions.toggleExpandedEvent({ timelineId })); dispatch( timelineActions.setActiveTabTimeline({ id: timelineId, activeTab: TimelineTabs.query }) ); }, [dispatch, timelineId]); const setGraphAsActiveTab = useCallback(() => { - dispatch(timelineActions.toggleExpandedEvent({ timelineId })); dispatch( timelineActions.setActiveTabTimeline({ id: timelineId, activeTab: TimelineTabs.graph }) ); }, [dispatch, timelineId]); const setNotesAsActiveTab = useCallback(() => { - dispatch(timelineActions.toggleExpandedEvent({ timelineId })); dispatch( timelineActions.setActiveTabTimeline({ id: timelineId, activeTab: TimelineTabs.notes }) ); }, [dispatch, timelineId]); const setPinnedAsActiveTab = useCallback(() => { - dispatch(timelineActions.toggleExpandedEvent({ timelineId })); dispatch( timelineActions.setActiveTabTimeline({ id: timelineId, activeTab: TimelineTabs.pinned }) ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/selectors.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/selectors.ts index 332785161b09a..ff65c35588a8d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/selectors.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/selectors.ts @@ -5,8 +5,8 @@ */ import { createSelector } from 'reselect'; +import { TimelineTabs } from '../../../../../common/types/timeline'; import { selectNotesById } from '../../../../common/store/app/selectors'; -import { TimelineTabs } from '../../../store/timeline/model'; import { selectTimeline } from '../../../store/timeline/selectors'; export const getActiveTabSelector = () => @@ -18,5 +18,11 @@ export const getShowTimelineSelector = () => export const getPinnedEventSelector = () => createSelector(selectTimeline, (timeline) => Object.keys(timeline?.pinnedEventIds ?? {}).length); +export const getNoteIdsSelector = () => + createSelector(selectTimeline, (timeline) => timeline?.noteIds ?? []); + +export const getEventIdToNoteIdsSelector = () => + createSelector(selectTimeline, (timeline) => timeline?.eventIdToNoteIds ?? {}); + export const getNotesSelector = () => - createSelector(selectNotesById, (notesById) => Object.keys(notesById ?? {}).length); + createSelector(selectNotesById, (notesById) => Object.values(notesById)); diff --git a/x-pack/plugins/security_solution/public/timelines/containers/active_timeline_context.ts b/x-pack/plugins/security_solution/public/timelines/containers/active_timeline_context.ts index 287fcd7f11e93..3d6d061157b29 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/active_timeline_context.ts +++ b/x-pack/plugins/security_solution/public/timelines/containers/active_timeline_context.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { TimelineExpandedEvent } from '../../../common/types/timeline'; +import { TimelineExpandedEventType } from '../../../common/types/timeline'; import { TimelineEventsAllRequestOptions } from '../../../common/search_strategy/timeline'; import { TimelineArgs } from '.'; @@ -21,7 +21,7 @@ import { TimelineArgs } from '.'; class ActiveTimelineEvents { private _activePage: number = 0; - private _expandedEvent: TimelineExpandedEvent = {}; + private _expandedEvent: TimelineExpandedEventType = {}; private _pageName: string = ''; private _request: TimelineEventsAllRequestOptions | null = null; private _response: TimelineArgs | null = null; @@ -38,7 +38,7 @@ class ActiveTimelineEvents { return this._expandedEvent; } - toggleExpandedEvent(expandedEvent: TimelineExpandedEvent) { + toggleExpandedEvent(expandedEvent: TimelineExpandedEventType) { if (expandedEvent.eventId === this._expandedEvent.eventId) { this._expandedEvent = {}; } else { @@ -46,7 +46,7 @@ class ActiveTimelineEvents { } } - setExpandedEvent(expandedEvent: TimelineExpandedEvent) { + setExpandedEvent(expandedEvent: TimelineExpandedEventType) { this._expandedEvent = expandedEvent; } diff --git a/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx index ebc86b3c5cf5e..556221f2d4bfd 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { noop } from 'lodash/fp'; +import { isEmpty, noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; import deepEqual from 'fast-deep-equal'; @@ -18,6 +18,7 @@ import { TimelineEventsDetailsStrategyResponse, } from '../../../../common/search_strategy'; import { isCompleteResponse, isErrorResponse } from '../../../../../../../src/plugins/data/public'; +import { AbortError } from '../../../../../../../src/plugins/kibana_utils/common'; export interface EventsArgs { detailsData: TimelineEventsDetailsItem[] | null; } @@ -50,7 +51,7 @@ export const useTimelineEventsDetails = ({ const timelineDetailsSearch = useCallback( (request: TimelineEventsDetailsRequestOptions | null) => { - if (request == null || skip) { + if (request == null || skip || isEmpty(request.eventId)) { return; } @@ -84,11 +85,13 @@ export const useTimelineEventsDetails = ({ searchSubscription$.unsubscribe(); } }, - error: () => { + error: (msg) => { if (!didCancel) { setLoading(false); } - notifications.toasts.addDanger('Failed to run search'); + if (!(msg instanceof AbortError)) { + notifications.toasts.addDanger('Failed to run search'); + } }, }); }; diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts index 487dc171f5994..aefeda04dd962 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts @@ -15,13 +15,15 @@ import { } from '../../../timelines/components/timeline/data_providers/data_provider'; import { SerializedFilterQuery } from '../../../common/store/types'; -import { KqlMode, TimelineModel, ColumnHeaderOptions, TimelineTabs } from './model'; +import { KqlMode, TimelineModel, ColumnHeaderOptions } from './model'; import { TimelineNonEcsData } from '../../../../common/search_strategy/timeline'; import { TimelineEventsType, - TimelineExpandedEvent, + TimelineExpandedEventType, TimelineTypeLiteral, RowRendererId, + TimelineExpandedEvent, + TimelineTabs, } from '../../../../common/types/timeline'; import { InsertTimeline } from './types'; @@ -36,8 +38,9 @@ export const addNoteToEvent = actionCreator<{ id: string; noteId: string; eventI ); export interface ToggleExpandedEvent { + event?: TimelineExpandedEventType; + tabType?: TimelineTabs; timelineId: string; - event?: TimelineExpandedEvent; } export const toggleExpandedEvent = actionCreator('TOGGLE_EXPANDED_EVENT'); diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts index 211bba3cc47d2..fd0d6bd3a9aaa 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { TimelineType, TimelineStatus } from '../../../../common/types/timeline'; +import { TimelineType, TimelineStatus, TimelineTabs } from '../../../../common/types/timeline'; import { Direction } from '../../../graphql/types'; import { defaultHeaders } from '../../components/timeline/body/column_headers/default_headers'; import { normalizeTimeRange } from '../../../common/components/url_state/normalize_time_range'; -import { SubsetTimelineModel, TimelineModel, TimelineTabs } from './model'; +import { SubsetTimelineModel, TimelineModel } from './model'; // normalizeTimeRange uses getTimeRangeSettings which cannot be used outside Kibana context if the uiSettings is not false const { from: start, to: end } = normalizeTimeRange({ from: '', to: '' }, false); diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.test.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.test.ts index d890fbe6a1069..ec9ded610417f 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.test.ts @@ -5,10 +5,10 @@ */ import { Filter, esFilters } from '../../../../../../../src/plugins/data/public'; -import { TimelineType, TimelineStatus } from '../../../../common/types/timeline'; +import { TimelineType, TimelineStatus, TimelineTabs } from '../../../../common/types/timeline'; import { Direction } from '../../../graphql/types'; import { convertTimelineAsInput } from './epic'; -import { TimelineModel, TimelineTabs } from './model'; +import { TimelineModel } from './model'; describe('Epic Timeline', () => { describe('#convertTimelineAsInput ', () => { diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx index 3014ae8d19d32..513d61ea862fa 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx @@ -40,8 +40,7 @@ import { Direction } from '../../../graphql/types'; import { addTimelineInStorage } from '../../containers/local_storage'; import { isPageTimeline } from './epic_local_storage'; -import { TimelineId, TimelineStatus } from '../../../../common/types/timeline'; -import { TimelineTabs } from './model'; +import { TimelineId, TimelineStatus, TimelineTabs } from '../../../../common/types/timeline'; jest.mock('../../containers/local_storage'); diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts index f3ff3fffa53b9..c385f21153780 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts @@ -17,11 +17,11 @@ import type { TimelineType, TimelineStatus, RowRendererId, + TimelineTabs, } from '../../../../common/types/timeline'; export const DEFAULT_PAGE_COUNT = 2; // Eui Pager will not render unless this is a minimum of 2 pages export type KqlMode = 'filter' | 'search'; - export type ColumnHeaderType = 'not-filtered' | 'text-filter'; /** Uniquely identifies a column */ @@ -43,13 +43,6 @@ export interface ColumnHeaderOptions { width: number; } -export enum TimelineTabs { - query = 'query', - graph = 'graph', - notes = 'notes', - pinned = 'pinned', -} - export interface TimelineModel { /** The selected tab to displayed in the timeline */ activeTab: TimelineTabs; diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts index 59d5800271b8a..4ae271ed7a491 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts @@ -5,7 +5,7 @@ */ import { cloneDeep } from 'lodash/fp'; -import { TimelineType, TimelineStatus } from '../../../../common/types/timeline'; +import { TimelineType, TimelineStatus, TimelineTabs } from '../../../../common/types/timeline'; import { IS_OPERATOR, @@ -40,7 +40,7 @@ import { updateTimelineTitle, upsertTimelineColumn, } from './helpers'; -import { ColumnHeaderOptions, TimelineModel, TimelineTabs } from './model'; +import { ColumnHeaderOptions, TimelineModel } from './model'; import { timelineDefaults } from './defaults'; import { TimelineById } from './types'; diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts index 8ba4d54871266..2603c1c677956 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts @@ -103,7 +103,7 @@ import { } from './helpers'; import { TimelineState, EMPTY_TIMELINE_BY_ID } from './types'; -import { TimelineType } from '../../../../common/types/timeline'; +import { TimelineType, TimelineTabs } from '../../../../common/types/timeline'; export const initialTimelineState: TimelineState = { timelineById: EMPTY_TIMELINE_BY_ID, @@ -178,16 +178,22 @@ export const timelineReducer = reducerWithInitialState(initialTimelineState) ...state, timelineById: addTimelineNoteToEvent({ id, noteId, eventId, timelineById: state.timelineById }), })) - .case(toggleExpandedEvent, (state, { timelineId, event = {} }) => ({ - ...state, - timelineById: { - ...state.timelineById, - [timelineId]: { - ...state.timelineById[timelineId], - expandedEvent: event, + .case(toggleExpandedEvent, (state, { tabType, timelineId, event = {} }) => { + const expandedTabType = tabType ?? TimelineTabs.query; + return { + ...state, + timelineById: { + ...state.timelineById, + [timelineId]: { + ...state.timelineById[timelineId], + expandedEvent: { + ...state.timelineById[timelineId].expandedEvent, + [expandedTabType]: event, + }, + }, }, - }, - })) + }; + }) .case(addProvider, (state, { id, provider }) => ({ ...state, timelineById: addTimelineProvider({ id, provider, timelineById: state.timelineById }), diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver.ts index 42a69d7b1e964..ac6d4398f3be3 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver.ts @@ -7,19 +7,12 @@ import { IRouter } from 'kibana/server'; import { EndpointAppContext } from '../types'; import { - validateTreeEntityID, validateEvents, - validateChildren, - validateAncestry, - validateAlerts, validateEntities, validateTree, } from '../../../common/endpoint/schema/resolver'; -import { handleChildren } from './resolver/children'; -import { handleAncestry } from './resolver/ancestry'; -import { handleTree as handleTreeEntityID } from './resolver/tree'; + import { handleTree } from './resolver/tree/handler'; -import { handleAlerts } from './resolver/alerts'; import { handleEntities } from './resolver/entity'; import { handleEvents } from './resolver/events'; @@ -44,54 +37,6 @@ export function registerResolverRoutes(router: IRouter, endpointAppContext: Endp handleEvents(log) ); - /** - * @deprecated will be removed because it is not used - */ - router.post( - { - path: '/api/endpoint/resolver/{id}/alerts', - validate: validateAlerts, - options: { authRequired: true }, - }, - handleAlerts(log, endpointAppContext) - ); - - /** - * @deprecated use the /resolver/tree api instead - */ - router.get( - { - path: '/api/endpoint/resolver/{id}/children', - validate: validateChildren, - options: { authRequired: true }, - }, - handleChildren(log, endpointAppContext) - ); - - /** - * @deprecated use the /resolver/tree api instead - */ - router.get( - { - path: '/api/endpoint/resolver/{id}/ancestry', - validate: validateAncestry, - options: { authRequired: true }, - }, - handleAncestry(log, endpointAppContext) - ); - - /** - * @deprecated use the /resolver/tree api instead - */ - router.get( - { - path: '/api/endpoint/resolver/{id}', - validate: validateTreeEntityID, - options: { authRequired: true }, - }, - handleTreeEntityID(log, endpointAppContext) - ); - /** * Used to get details about an entity, aka process. */ diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/alerts.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/alerts.ts deleted file mode 100644 index 8e641194ab899..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/alerts.ts +++ /dev/null @@ -1,41 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { TypeOf } from '@kbn/config-schema'; -import { RequestHandler, Logger } from 'kibana/server'; -import { validateAlerts } from '../../../../common/endpoint/schema/resolver'; -import { alertsIndexPattern, eventsIndexPattern } from '../../../../common/endpoint/constants'; -import { Fetcher } from './utils/fetch'; -import { EndpointAppContext } from '../../types'; - -export function handleAlerts( - log: Logger, - endpointAppContext: EndpointAppContext -): RequestHandler< - TypeOf, - TypeOf, - TypeOf -> { - return async (context, req, res) => { - const { - params: { id }, - query: { alerts, afterAlert, legacyEndpointID: endpointID }, - body, - } = req; - try { - const client = context.core.elasticsearch.legacy.client; - - const fetcher = new Fetcher(client, id, eventsIndexPattern, alertsIndexPattern, endpointID); - - return res.ok({ - body: await fetcher.alerts(alerts, afterAlert, body?.filter), - }); - } catch (err) { - log.warn(err); - return res.internalError({ body: err }); - } - }; -} diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/ancestry.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/ancestry.ts deleted file mode 100644 index cff80ec5010bd..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/ancestry.ts +++ /dev/null @@ -1,37 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { RequestHandler, Logger } from 'kibana/server'; -import { TypeOf } from '@kbn/config-schema'; -import { eventsIndexPattern, alertsIndexPattern } from '../../../../common/endpoint/constants'; -import { validateAncestry } from '../../../../common/endpoint/schema/resolver'; -import { Fetcher } from './utils/fetch'; -import { EndpointAppContext } from '../../types'; - -export function handleAncestry( - log: Logger, - endpointAppContext: EndpointAppContext -): RequestHandler, TypeOf> { - return async (context, req, res) => { - const { - params: { id }, - query: { ancestors, legacyEndpointID: endpointID }, - } = req; - try { - const client = context.core.elasticsearch.legacy.client; - - const fetcher = new Fetcher(client, id, eventsIndexPattern, alertsIndexPattern, endpointID); - const ancestorInfo = await fetcher.ancestors(ancestors); - - return res.ok({ - body: ancestorInfo, - }); - } catch (err) { - log.warn(err); - return res.internalError({ body: err }); - } - }; -} diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/children.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/children.ts deleted file mode 100644 index 9b8cd9fd3edab..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/children.ts +++ /dev/null @@ -1,35 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { RequestHandler, Logger } from 'kibana/server'; -import { TypeOf } from '@kbn/config-schema'; -import { eventsIndexPattern, alertsIndexPattern } from '../../../../common/endpoint/constants'; -import { validateChildren } from '../../../../common/endpoint/schema/resolver'; -import { Fetcher } from './utils/fetch'; -import { EndpointAppContext } from '../../types'; - -export function handleChildren( - log: Logger, - endpointAppContext: EndpointAppContext -): RequestHandler, TypeOf> { - return async (context, req, res) => { - const { - params: { id }, - query: { children, afterChild, legacyEndpointID: endpointID }, - } = req; - try { - const client = context.core.elasticsearch.legacy.client; - const fetcher = new Fetcher(client, id, eventsIndexPattern, alertsIndexPattern, endpointID); - - return res.ok({ - body: await fetcher.children(children, afterChild), - }); - } catch (err) { - log.warn(err); - return res.internalError({ body: err }); - } - }; -} diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/docs/README.md b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/docs/README.md index 1c0692db344c4..4cbbf377b8c92 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/docs/README.md +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/docs/README.md @@ -2,6 +2,12 @@ This readme will describe the backend implementation for resolver. +## APIs + +The `/tree` API only retrieves a single lifecycle event per node in the tree. This allows us to avoid hitting the Elasticsearch 10k document limit a little easier. If we didn't do this, it's possible that a single node in the tree could have 10k lifecycle events which means the API would only return a tree of a single node. + +The `/events` API provides the ability retrieve all the lifecycle events for a node or a set of nodes. + ## Ancestry Array The ancestry array is an array of entity_ids. This array is included with each event sent by the elastic endpoint @@ -26,15 +32,11 @@ the next set of results to fulfill the request. ### Pagination -After the backend gathers the results for an ancestry query, it will set a pagination cursor depending on the results from ES. - -If the number of ancestors we have gathered is equal to the size in the request we don't know if ES has more results or not. So we will set `nextAncestor` to the entity_id of the most distant ancestor retrieved. - -If the request asked for 10 and we only found 8 from ES, we know for sure that there aren't anymore results. In this case we will set `nextAncestor` to `null`. +There is no pagination cursor for the `/tree` API. The caller should make multiple requests as needed using the most distant ancestor received from previous requests to retrieve additional ancestors. ### Code -The code for handling the ancestor logic is in [here](../utils/ancestry_query_handler.ts) +The code for handling the ancestor logic is in [here](../tree/utils/fetch.ts) ### Ancestors Multiple Queries Example @@ -42,7 +44,15 @@ The code for handling the ancestor logic is in [here](../utils/ancestry_query_ha For this example let's assume that the _ancestry array limit_ is 2. The process of interest is A (the entity_id of a node is the character in the circle). Process A has an ancestry array of `[3,2]`, its parent has an ancestry array of `[2,1]` etc. Here is the execution of a request for 3 ancestors for entity_id A. -**Request:** `GET /resolver/A/ancestry?ancestors=3` +**Request:** + +``` +POST /resolver/tree +{ + ancestors: 3, + nodes: [A], +} +``` 1. Retrieve lifecycle events for entity_id `A` 2. Retrieve `A`'s start event's ancestry array @@ -55,35 +65,31 @@ For this example let's assume that the _ancestry array limit_ is 2. The process If process 2 had an ancestry array of `[1,0]` we know that we only need 1 more process to fulfill the request so we can truncate the array to `[1]` instead of searching for all the entries in the array. -More generically: In the event where our request stops at the x (non-final) position in an ancestry array, we won't search all items in the array, just those up to the x position. The next-cursor will be set to the last ancestor received since there might be more data. - -The `nextAncestor` cursor will be set to `1` in this scenario because we retrieved all 3 ancestors from ES but we don't know if ES has anymore. - ## Descendants We can also leverage the ancestry array to query for the descendants of a process. The basic query for the descendants of a process is: _find all processes where their ancestry array contains a particular entity_id_. The results of this query will be sorted in ascending order by the timestamp field. I will try to outline a couple different scenarios for retrieving descendants using the ancestry array below. -### Start events vs all lifecycle events - -There are two parts to querying for descendant process nodes. When a request comes in for 7 process nodes we need to communicate to ES that we want all of the lifecycle nodes for 7 processes. We could use a query that retrieves all lifecycle events (start, end, etc) but the issue with this is that we need to indicate a `size` in our ES query. If we set the `size` to 7, we will only get 7 lifecycle events. These events could be start, end, or already_running events. It doesn't guarantee that we get all of the lifecycle events for 7 process nodes. - -Instead we can first query for 7 start events, which guarantees that we will have 7 unique process descendants and then we can gather all those entity_ids and do another query for all the lifecycle events for those 7 processes. The downside here is that you have to do two queries to retrieve all the lifecycle events. Optimizations can be made for the first query for the start events by reducing the `_source` that ES returns to only include the `entity_id` and `ancestry`. This will reduce the amount of data that ES has to send back and speed up the query. - ### Scenario Background In the scenarios below let's assume the _ancestry array limit_ is 2. The times next to the nodes are the time the node was spawned. The value in red indicates that the process terminated at the time in red. -Let's also ignore the fact that retrieving the lifecycle events for a descendant actually takes two queries. Let's assume that it's taken care of, and when we say "query for lifecycle events" we get all the lifecycle events back for the descendants using the algorithm described in the [previous section](#start-events-vs-all-lifecycle-events) - ### Simple Scenario For reference the time based order of the being nodes being spawned in this scenario is: A -> B -> C -> E -> F -> G -> H. ![alt text](./resolver_tree_children_simple.png 'Descendants Simple Scenario') -**Request:** `GET /resolver/A/children?children=6` +**Request:** -For this scenario we will retrieve all the lifecycle events for 6 descendants of the process with entity_id `A`. As shown in the diagram above ES has 6 descendants for A so the response to this request will be: `[B, C, E, F, G, H]` because the results are sorted in ascending ordering based on the `timestamp` field which is when the process was started. +``` +POST /resolver/tree +{ + descendants: 6, + nodes: [A], +} +``` + +For this scenario we will retrieve one lifecycle event for 6 descendants of the process with entity_id `A`. As shown in the diagram above ES has 6 descendants for A so the response to this request will be: `[B, C, E, F, G, H]` because the results are sorted in ascending ordering based on the `timestamp` field which is when the process was started. ### Looping Scenario @@ -91,7 +97,15 @@ For reference the time based order of the being nodes being spawned in this scen ![alt text](./resolver_tree_children_loop.png 'Descendants Looping Scenario') -**Request:** `GET /resolver/A/children?children=9` +**Request:** + +``` +POST /resolver/tree +{ + descendants: 9, + nodes: [A] +} +``` In this scenario the request is for more descendants than can be retrieved using a single querying with the entity_id `A`. This is because the ancestry array for the descendants in the red section do not have `A` in their ancestry array. So when we query for all process nodes that have `A` in their ancestry array we won't receive D, J, or K. @@ -109,6 +123,8 @@ We have now received all the nodes for the request and we can return the results In the previous example the final results are not sorted based on timestamp in ascending order. This is because we had to perform multiple queries to retrieve all the results. The backend will not return the results in sorted order. +The backend will return the nodes in breadth-first order. If the requested node has more than 10k immediate children then the response will only contain those children even if they also have descendants. + #### Tie breaks on timestamp In the previous example we saw that J and K had the same timestamp of `12:13 pm`. The reason they were returned in the order `[J, K]` is because the `event.id` field is used to break ties like this. The `event.id` field is unique for a particular event and an increasing value per ECS's guidelines. Therefore J comes before K because it has an `event.id` of 1 vs 2. @@ -128,89 +144,14 @@ This edge case will likely never be encountered but we'll try to solve it anyway While we are iterating we also keep track of the largest ancestry array that we have seen. In our scenario that will be a size of 2. Then to determine the distant descendants we simply get the nodes that had the largest ancestry array length. In this scenario that'd be `[C, E, F, H]`. -### Handling Pagination - -#### Pagination Cursor Values - -There are 3 possible states for the pagination cursor for a child node and 2 possible states for the pagination cursor for the node of interest (the node that we are using in the API request). - -Potential cursors for the node of interest: -**a string cursor:** a cursor that can be used to skip the previous set of results. The cursor is a base64 version of a json object with `event.id` and `timestamp` of the last process's start event that was received. -**null:** indicates that no more results can be received using this process's entity_id - -Potential cursors for descendants of the node of interest (these apply to the results of a request): -**a string cursor:** a cursor that can be used to skip the previous set of results. The cursor is a base64 version of a json object with `event.id` and `timestamp` of the last process's start event that was received. This cursor should be used in conjunction with using this process's entity_id for a query. -**undefined:** the node may contain additional children, but we are not aware. To find out, perform additional queries on the node of interest that original returned these results or move down the tree to a descendant of this node to query for more descendants. -**null:** We have found all possible direct children for this node. There may be more descendants but not direct children for this node. - -#### Pagination Examples +#### Handling Pagination -For reference the time based order of the being nodes being spawned in this scenario is: A -> B -> C -> D -> G -> F -> H -> E -> J -> K. +The `/tree` API does not support pagination. To retrieve additional nodes in the tree the requestor can use a specific node from a previous request (ideally a leaf node) and make an additional request to the `/tree` API to retrieve that node's descendants. -![alt text](./resolver_tree_children_pagination.png 'Descendants Pagination Scenario') +#### Finding Siblings -Handling pagination for the children API in a little tricky. Let's consider this scenario: - -**Request:** `GET /resolver/A/children?children=3` - -Let's use the diagram above to show the relationship between processes and the data in ES. The response for the request for 3 children is `[B, C, G]`. More process nodes exist in ES so it would be helpful to indicate in the response that there is more data and a way to skip `[B, C, G]` to get the next set of data. A cursor can be set on the response for `A` to point to the last process node in the response which can be sent in another request to retrieve the next set of data. This cursor will contain information from `G` because it has the latest timestamp (in ascending order). - -If another request was made using the returned cursor like the following: - -**Request:** `GET /resolver/A/children?children=5&after=` - -For reference the time based order of the being nodes being spawned in this scenario is: A -> B -> C -> D -> G -> F -> H -> E -> J -> K (the same as above). - -![alt text](./resolver_tree_children_pagination_with_after.png 'Descendants Pagination Scenario Part 2') - -For this request we will do a query for _find all process nodes where their ancestry array has entity_id `A` and use the cursor to skip old results_. The response for this request is `[F, H, E, K]`. The request actually asked for 5 nodes but there was only 4 in ES so only 4 were returned. - -The odd thing about this response is that it did not receive D and J. The problem is that the backend does not have any concept of C in this second request because it was received in the previous one. It will be skipped based on the pagination cursor returned previously. - -This example highlights a scenario where it is not easy for the backend to go back and continue to get the descendants for C because of the limitation of the ancestry array. - -#### Pagination cursor for descendant nodes - -Let's go back to the first request where we got `[B, C, G]`. How could we go about getting the rest of the children for `B`? We have two ways of solving this. First we could determine what the last descendant we had received of `B` and use that as the cursor when returning all the results for this request. That is actually kind of difficult. - -For reference the time based order of the being nodes being spawned in this scenario is: A -> B -> C -> D -> G -> F -> H -> E -> J -> K (the same as above). - -![alt text](./resolver_tree_children_pagination.png 'Descendants Pagination Scenario') - -Let's imagine for a moment that the _ancestry array limit_ is 3 instead of 2. Taking our previous request we would instead get `[B, C, D]` because D started before G. In this case the last descendant for `B` is actually `D` and not `C`. This gets complicated because we'd have to keep track of which descendant was the last (time wise) one for each intermediate process node. In this example we'd need to find the last descendant for both `B` and `C`. We'd have to track the descendants for each process node and build a map to quickly be able to retrieve the last descendant. - -Instead of doing that we could also continue to get the immediate children of `B` by doing another request for `A` like was shown in previous example when using the after cursor. This would guarantee that all children (first level descendants of a node) had been retrieved. - -For reference the time based order of the being nodes being spawned in this scenario is: A -> B -> C -> D -> G -> F -> H -> E -> J -> K (the same as above). - -![alt text](./resolver_tree_children_pagination_with_after.png 'Descendants Pagination Scenario Part 2') - -To get to the response shown in the diagram above (the blue nodes is the response) a request was made for 3 nodes which returned `[B, C, D]` and then another request was made using the returned cursor for `A` to get an additional 4 nodes `[F, H, E, K]`. Let's assume that the request looked like: - -**Request:** `GET /resolver/A/children?children=5&after=` - -So 5 nodes were actually asked for. After `[F, H, E]` are returned during the first query to ES, we will use the most distant children (also `[F, H, E]`) and make another request for any nodes that have F or H or E in their ancestry array. Only a single node satisfies that query which returns `K`. Therefore we can know with certainty that E, F, and H have no more children because ES only returned K instead of K and one more node (since we requested a total of 5). With this knowledge we can mark A, E, F, H's pagination cursors in a way to communicate that they have no more descendants. - -The way the backend communicates this is by marking the cursor as null. - -If the request was actually for only 4 children like: - -**Request:** `GET /resolver/A/children?children=4&after=` - -Then we wouldn't know for sure whether ES had more results than K. But what we can know is that `A` does not have any more descendants that we can retrieve in a single query using its ancestry array. We have received all nodes where `A` is in their ancestry array when we made the second query for nodes E, F, and H and received `K`. Therefore at the moment when we received `[F, H, E]` we can mark `A`'s cursor as null. - -When we make the next query for any nodes that have F or H or E in their ancestry array and get back K, this satisfies our size of only needing one more node (4 total). At this point we don't know for sure if E, F, or H have more descendants. Since we don't know we will mark E, F, and H's cursors to point to K. - -#### Undefined Pagination - -For reference the time based order of the being nodes being spawned in this scenario is: A -> B -> C -> D -> E -> F -> G -> J -> K -> H. (Not the same as above). - -For this scenario let's assume ES has the data in the diagram below. Let's say the request looks like: - -**Request:** `GET /resolver/A/children?children=6` - -![alt text](./resolver_tree_children_loop.png 'Descendants Looping Scenario') +Currently there is no support for finding siblings of a node. This is an area that needs improvement. If a request is made for descendants of a node and 10,001 children are found, only the first 10k children will be returned. The last children will not be returned. -The result for this request will be `[B, C, E, F, G, H]`. Since the request was looking for 6 nodes and we got that amount the cursor for `A` will be set to the last one: `H`. The cursors for the intermediate nodes `B` and `G` will be undefined. This is because we don't know if `B` or `G` have more children but `A` can be used to determine that. We also don't know if C, E, F, or H have more descendants so their cursor will also be marked as undefined. If we wanted to know if C had more descendants we can simply issue a new request like `GET /resolver/C/children` to get its descendants and we won't receive and duplicates because we never received D, J or K. +A possible solution using the current `/tree` API is to make another request but set the time range filter to be after the child with the most recent `@timestamp` value (since the events are sorted in ascending order on the `@timestamp` field). The problem with this approach is that even if we use a different time range, the first 10k nodes could have other lifecycle events that fall within the new time range and those nodes would be returned again. -If we want to know if `B` has more children we can issue another request using the cursor set for `A`. +Ideally the backend would supply a `/siblings` route that could return siblings of a node in a specific direction perhaps. diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/events.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/events.ts index db44bbd3d916c..5146591a77996 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/events.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/events.ts @@ -6,11 +6,24 @@ import { TypeOf } from '@kbn/config-schema'; import { RequestHandler, Logger } from 'kibana/server'; +import { ResolverPaginatedEvents, SafeResolverEvent } from '../../../../common/endpoint/types'; import { validateEvents } from '../../../../common/endpoint/schema/resolver'; import { EventsQuery } from './queries/events'; -import { createEvents } from './utils/node'; import { PaginationBuilder } from './utils/pagination'; +/** + * Creates an object that the events handler would return + * + * @param events array of events + * @param nextEvent the cursor to retrieve the next event + */ +function createEvents( + events: SafeResolverEvent[] = [], + nextEvent: string | null = null +): ResolverPaginatedEvents { + return { events, nextEvent }; +} + /** * This function handles the `/events` api and returns an array of events and a cursor if more events exist than were * requested. diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/alerts.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/alerts.ts deleted file mode 100644 index 8f68cba893108..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/alerts.ts +++ /dev/null @@ -1,74 +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; - * you may not use this file except in compliance with the Elastic License. - */ -import { SearchResponse } from 'elasticsearch'; -import { esKuery } from '../../../../../../../../src/plugins/data/server'; -import { SafeResolverEvent } from '../../../../../common/endpoint/types'; -import { ResolverQuery } from './base'; -import { PaginationBuilder } from '../utils/pagination'; -import { JsonObject } from '../../../../../../../../src/plugins/kibana_utils/common'; - -/** - * Builds a query for retrieving alerts for a node. - */ -export class AlertsQuery extends ResolverQuery { - private readonly kqlQuery: JsonObject[] = []; - constructor( - private readonly pagination: PaginationBuilder, - indexPattern: string | string[], - endpointID?: string, - kql?: string - ) { - super(indexPattern, endpointID); - if (kql) { - this.kqlQuery.push(esKuery.toElasticsearchQuery(esKuery.fromKueryExpression(kql))); - } - } - - protected legacyQuery(endpointID: string, uniquePIDs: string[]): JsonObject { - return { - query: { - bool: { - filter: [ - ...this.kqlQuery, - { - terms: { 'endgame.unique_pid': uniquePIDs }, - }, - { - term: { 'agent.id': endpointID }, - }, - { - term: { 'event.kind': 'alert' }, - }, - ], - }, - }, - ...this.pagination.buildQueryFields('endgame.serial_event_id', 'asc'), - }; - } - - protected query(entityIDs: string[]): JsonObject { - return { - query: { - bool: { - filter: [ - ...this.kqlQuery, - { - terms: { 'process.entity_id': entityIDs }, - }, - { - term: { 'event.kind': 'alert' }, - }, - ], - }, - }, - ...this.pagination.buildQueryFields('event.id', 'asc'), - }; - } - - formatResponse(response: SearchResponse): SafeResolverEvent[] { - return this.getResults(response); - } -} diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/base.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/base.ts deleted file mode 100644 index a2bdf358745c2..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/base.ts +++ /dev/null @@ -1,117 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { SearchResponse } from 'elasticsearch'; -import { ILegacyScopedClusterClient } from 'kibana/server'; -import { SafeResolverEvent } from '../../../../../common/endpoint/types'; -import { JsonObject } from '../../../../../../../../src/plugins/kibana_utils/common'; -import { legacyEventIndexPattern } from './legacy_event_index_pattern'; -import { MSearchQuery } from './multi_searcher'; - -/** - * ResolverQuery provides the base structure for queries to retrieve events when building a resolver graph. - * - * @param T the structured return type of a resolver query. This represents the final return type of the query after handling - * any aggregations. - * @param R the is the type after transforming ES's response. Making this definable let's us set whether it is a resolver event - * or something else. - */ -export abstract class ResolverQuery implements MSearchQuery { - /** - * - * @param indexPattern the index pattern to use in the query for finding indices with documents in ES. - * @param endpointID this field is optional because it is only used when searching for legacy event data. The reason - * we need `endpointID` for legacy data is because we don't have a cross endpoint unique identifier for process - * events. Instead we use `unique_pid/ppid` and `endpointID` to uniquely identify a process event. - */ - constructor( - private readonly indexPattern: string | string[], - private readonly endpointID?: string - ) {} - - private static createIdsArray(ids: string | string[]): string[] { - return Array.isArray(ids) ? ids : [ids]; - } - - private buildQuery(ids: string | string[]): { query: JsonObject; index: string | string[] } { - // only accept queries for entity_ids that are not an empty string - const idsArray = ResolverQuery.createIdsArray(ids).filter((id) => id !== ''); - if (this.endpointID) { - return { query: this.legacyQuery(this.endpointID, idsArray), index: legacyEventIndexPattern }; - } - return { query: this.query(idsArray), index: this.indexPattern }; - } - - private buildSearch(ids: string | string[]) { - const { query, index } = this.buildQuery(ids); - return { - body: query, - index, - }; - } - - protected getResults(response: SearchResponse): R[] { - return response.hits.hits.map((hit) => hit._source); - } - - /** - * Builds a multi search representation for this query - * - * @param ids a single or multiple unique id (e.g. entity_id for new events or unique_pid for legacy events) to search for in the query - * @returns an array of header and body pairs that represents a multi search - * https://www.elastic.co/guide/en/elasticsearch/reference/current/search-multi-search.html - * https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/msearch_examples.html - */ - buildMSearch(ids: string | string[]): JsonObject[] { - const { query, index } = this.buildQuery(ids); - return [{ index }, query]; - } - - /** - * Searches ES for the specified ids and format the response. - * - * @param client a client for searching ES - * @param ids a single more multiple unique node ids (e.g. entity_id or unique_pid) - */ - async searchAndFormat(client: ILegacyScopedClusterClient, ids: string | string[]): Promise { - const res: SearchResponse = await this.search(client, ids); - return this.formatResponse(res); - } - - /** - * Searches ES for the specified ids but do not format the response. - * - * @param client a client for searching ES - * @param ids a single more multiple unique node ids (e.g. entity_id or unique_pid) - */ - async search(client: ILegacyScopedClusterClient, ids: string | string[]) { - return client.callAsCurrentUser('search', this.buildSearch(ids)); - } - - /** - * Builds a query to search the legacy data format. - * - * @param endpointID a unique identifier for a sensor - * @param uniquePIDs array of unique process IDs to search for - * @returns a query to use in ES - */ - protected abstract legacyQuery(endpointID: string, uniquePIDs: string[]): JsonObject; - - /** - * Builds a query to search for events in ES. - * - * @param entityIDs array of unique identifiers for events treated as nodes - */ - protected abstract query(entityIDs: string[]): JsonObject; - - /** - * Translates the response from executing the derived class's query into a structured object - * - * @param response a SearchResponse from ES resulting from executing this query - * @returns the translated ES response into a structured object - */ - public abstract formatResponse(response: SearchResponse): T; -} diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/children.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/children.test.ts deleted file mode 100644 index 4e210e0237fcd..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/children.test.ts +++ /dev/null @@ -1,36 +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; - * you may not use this file except in compliance with the Elastic License. - */ -import { ChildrenQuery } from './children'; -import { ChildrenPaginationBuilder } from '../utils/children_pagination'; -import { legacyEventIndexPattern } from './legacy_event_index_pattern'; - -describe('Children query', () => { - it('constructs a legacy multi search query', () => { - const query = new ChildrenQuery( - new ChildrenPaginationBuilder(1), - 'index-pattern', - 'endpointID' - ); - // using any here because otherwise ts complains that it doesn't know what bool and filter are - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const msearch: any = query.buildMSearch('1234'); - expect(msearch[0].index).toBe(legacyEventIndexPattern); - expect(msearch[1].query.bool.filter[0]).toStrictEqual({ - terms: { 'endgame.unique_ppid': ['1234'] }, - }); - }); - - it('constructs a non-legacy multi search query', () => { - const query = new ChildrenQuery(new ChildrenPaginationBuilder(1), 'index-pattern'); - // using any here because otherwise ts complains that it doesn't know what bool and filter are - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const msearch: any = query.buildMSearch(['1234', '5678']); - expect(msearch[0].index).toBe('index-pattern'); - expect(msearch[1].query.bool.filter[0].bool.should[0]).toStrictEqual({ - terms: { 'process.parent.entity_id': ['1234', '5678'] }, - }); - }); -}); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/children.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/children.ts deleted file mode 100644 index 4c7be9b8d5a24..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/children.ts +++ /dev/null @@ -1,194 +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; - * you may not use this file except in compliance with the Elastic License. - */ -import { SearchResponse } from 'elasticsearch'; -import { ECSField } from '../../../../../common/endpoint/types'; -import { ResolverQuery } from './base'; -import { ChildrenPaginationBuilder } from '../utils/children_pagination'; -import { JsonObject } from '../../../../../../../../src/plugins/kibana_utils/common'; - -/** - * This type represents the document returned from ES for a legacy event when using the ChildrenQuery to fetch legacy events. - * It contains only the necessary fields that the children api needs to process the results before - * it requests the full lifecycle information for the children in a later query. - */ -export type LegacyChildEvent = Partial<{ - '@timestamp': ECSField; - event: Partial<{ - type: ECSField; - action: ECSField; - }>; - endgame: Partial<{ - serial_event_id: ECSField; - unique_pid: ECSField; - unique_ppid: ECSField; - }>; -}>; - -/** - * This type represents the document returned from ES for an event when using the ChildrenQuery to fetch legacy events. - * It contains only the necessary fields that the children api needs to process the results before - * it requests the full lifecycle information for the children in a later query. - */ -export type EndpointChildEvent = Partial<{ - '@timestamp': ECSField; - event: Partial<{ - type: ECSField; - sequence: ECSField; - }>; - process: Partial<{ - entity_id: ECSField; - parent: Partial<{ - entity_id: ECSField; - }>; - Ext: Partial<{ - ancestry: ECSField; - }>; - }>; -}>; - -export type ChildEvent = EndpointChildEvent | LegacyChildEvent; - -/** - * Builds a query for retrieving descendants of a node. - * The first type `ChildEvent[]` represents the final formatted result. The second type `ChildEvent` defines the type - * used in the `SearchResponse` field returned from the ES query. - */ -export class ChildrenQuery extends ResolverQuery { - constructor( - private readonly pagination: ChildrenPaginationBuilder, - indexPattern: string | string[], - endpointID?: string - ) { - super(indexPattern, endpointID); - } - - protected legacyQuery(endpointID: string, uniquePIDs: string[]): JsonObject { - const paginationFields = this.pagination.buildQueryFields('endgame.serial_event_id'); - return { - _source: [ - '@timestamp', - 'endgame.serial_event_id', - 'endgame.unique_pid', - 'endgame.unique_ppid', - 'event.type', - 'event.action', - ], - collapse: { - field: 'endgame.unique_pid', - }, - size: paginationFields.size, - sort: paginationFields.sort, - query: { - bool: { - filter: [ - ...paginationFields.filters, - { - terms: { 'endgame.unique_ppid': uniquePIDs }, - }, - { - term: { 'agent.id': endpointID }, - }, - { - term: { 'event.category': 'process' }, - }, - { - term: { 'event.kind': 'event' }, - }, - { - bool: { - should: [ - { - terms: { 'event.type': ['process_start', 'already_running'] }, - }, - { - term: { 'event.action': 'fork_event' }, - }, - ], - }, - }, - ], - }, - }, - }; - } - - protected query(entityIDs: string[]): JsonObject { - // we don't have to include the `event.id` in the source response because it is not needed for processing - // the data returned by ES, it is only used for breaking ties when ES is doing the search - const paginationFields = this.pagination.buildQueryFields('event.id'); - return { - _source: [ - '@timestamp', - 'event.type', - 'event.sequence', - 'process.entity_id', - 'process.parent.entity_id', - 'process.Ext.ancestry', - ], - /** - * Using collapse here will only return a single event per occurrence of a process.entity_id. The events are sorted - * based on timestamp in ascending order so it will be the first event that ocurred. The actual type of event that - * we receive for this query doesn't really matter (whether it is a start, info, or exec for a particular entity_id). - * All this is trying to accomplish is removing duplicate events that indicate a process existed for a node. We - * only need to know that a process existed and it's it's ancestry array and the process.entity_id fields because - * we will use it to query for the next set of descendants. - * - * The reason it is important to only receive 1 event per occurrence of a process.entity_id is it allows us to avoid - * ES 10k limit most of the time. If instead we received multiple events with the same process.entity_id that would - * reduce the maximum number of unique children processes we could retrieve in a single query. - */ - collapse: { - field: 'process.entity_id', - }, - size: paginationFields.size, - sort: paginationFields.sort, - query: { - bool: { - filter: [ - ...paginationFields.filters, - { - bool: { - should: [ - { - terms: { 'process.parent.entity_id': entityIDs }, - }, - { - terms: { 'process.Ext.ancestry': entityIDs }, - }, - ], - }, - }, - { - exists: { - field: 'process.entity_id', - }, - }, - { - bool: { - must_not: { - term: { 'process.entity_id': '' }, - }, - }, - }, - { - term: { 'event.category': 'process' }, - }, - { - term: { 'event.kind': 'event' }, - }, - { - terms: { 'event.type': ['start', 'info', 'change'] }, - }, - ], - }, - }, - }; - } - - formatResponse(response: SearchResponse): ChildEvent[] { - return this.getResults(response); - } -} diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/lifecycle.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/lifecycle.test.ts deleted file mode 100644 index f0a7f3bfa59c2..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/lifecycle.test.ts +++ /dev/null @@ -1,31 +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; - * you may not use this file except in compliance with the Elastic License. - */ -import { LifecycleQuery } from './lifecycle'; -import { legacyEventIndexPattern } from './legacy_event_index_pattern'; - -describe('Lifecycle query', () => { - it('constructs a legacy multi search query', () => { - const query = new LifecycleQuery('index-pattern', 'endpointID'); - // using any here because otherwise ts complains that it doesn't know what bool and filter are - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const msearch: any = query.buildMSearch('1234'); - expect(msearch[0].index).toBe(legacyEventIndexPattern); - expect(msearch[1].query.bool.filter[0]).toStrictEqual({ - terms: { 'endgame.unique_pid': ['1234'] }, - }); - }); - - it('constructs a non-legacy multi search query', () => { - const query = new LifecycleQuery('index-pattern'); - // using any here because otherwise ts complains that it doesn't know what bool and filter are - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const msearch: any = query.buildMSearch(['1234', '5678']); - expect(msearch[0].index).toBe('index-pattern'); - expect(msearch[1].query.bool.filter[0]).toStrictEqual({ - terms: { 'process.entity_id': ['1234', '5678'] }, - }); - }); -}); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/lifecycle.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/lifecycle.ts deleted file mode 100644 index ecbc5d8344928..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/lifecycle.ts +++ /dev/null @@ -1,65 +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; - * you may not use this file except in compliance with the Elastic License. - */ -import { SearchResponse } from 'elasticsearch'; -import { ResolverQuery } from './base'; -import { JsonObject } from '../../../../../../../../src/plugins/kibana_utils/common'; -import { SafeResolverEvent } from '../../../../../common/endpoint/types'; - -/** - * Builds a query for retrieving life cycle information about a node (start, stop, etc). - */ -export class LifecycleQuery extends ResolverQuery { - protected legacyQuery(endpointID: string, uniquePIDs: string[]): JsonObject { - return { - query: { - bool: { - filter: [ - { - terms: { 'endgame.unique_pid': uniquePIDs }, - }, - { - term: { 'agent.id': endpointID }, - }, - { - term: { 'event.kind': 'event' }, - }, - { - term: { 'event.category': 'process' }, - }, - ], - }, - }, - size: 10000, - sort: [{ '@timestamp': 'asc' }], - }; - } - - protected query(entityIDs: string[]): JsonObject { - return { - query: { - bool: { - filter: [ - { - terms: { 'process.entity_id': entityIDs }, - }, - { - term: { 'event.kind': 'event' }, - }, - { - term: { 'event.category': 'process' }, - }, - ], - }, - }, - size: 10000, - sort: [{ '@timestamp': 'asc' }], - }; - } - - formatResponse(response: SearchResponse): SafeResolverEvent[] { - return this.getResults(response); - } -} diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/multi_searcher.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/multi_searcher.ts deleted file mode 100644 index 76203973a6211..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/multi_searcher.ts +++ /dev/null @@ -1,83 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { ILegacyScopedClusterClient } from 'kibana/server'; -import { MSearchResponse, SearchResponse } from 'elasticsearch'; -import { SafeResolverEvent } from '../../../../../common/endpoint/types'; -import { JsonObject } from '../../../../../../../../src/plugins/kibana_utils/common'; - -/** - * Contract for queries to be compatible with ES multi search api - */ -export interface MSearchQuery { - /** - * Builds an array of header and body pairs for use in a multi search - * - * @param ids one or many unique identifiers for nodes. - * @returns an array of header and body pairs describing multi search queries - */ - buildMSearch(ids: string | string[]): JsonObject[]; -} - -/** - * Contract for adding a query for multi search - */ -export interface QueryInfo { - /** - * A multi search query - */ - query: MSearchQuery; - /** - * one or many unique identifiers to be searched for in this query - */ - ids: string | string[]; - /** - * a function to handle the response - */ - handler: (response: SearchResponse) => void; -} - -/** - * Executes a multi search within ES. - * - * More info on multi search here: - * https://www.elastic.co/guide/en/elasticsearch/reference/current/search-multi-search.html - * https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/msearch_examples.html - */ -export class MultiSearcher { - constructor(private readonly client: ILegacyScopedClusterClient) {} - - /** - * Perform the multi search on the passed in queries - * - * @param queries multi search queries - * @returns an array of SearchResponse - */ - async search(queries: QueryInfo[]) { - if (queries.length === 0) { - throw new Error('No queries provided to MultiSearcher'); - } - - const searchQuery: JsonObject[] = []; - for (const info of queries) { - searchQuery.push(...info.query.buildMSearch(info.ids)); - } - const res: MSearchResponse = await this.client.callAsCurrentUser('msearch', { - body: searchQuery, - }); - - if (!res.responses) { - throw new Error('No response from Elasticsearch'); - } - - if (res.responses.length !== queries.length) { - throw new Error(`Responses length was: ${res.responses.length} expected ${queries.length}`); - } - for (let i = 0; i < queries.length; i++) { - queries[i].handler(res.responses[i]); - } - } -} diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/stats.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/stats.test.ts deleted file mode 100644 index b6e8c6cdc08de..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/stats.test.ts +++ /dev/null @@ -1,31 +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; - * you may not use this file except in compliance with the Elastic License. - */ -import { StatsQuery } from './stats'; -import { legacyEventIndexPattern } from './legacy_event_index_pattern'; - -describe('Stats query', () => { - it('constructs a legacy multi search query', () => { - const query = new StatsQuery('index-pattern', 'endpointID'); - // using any here because otherwise ts complains that it doesn't know what bool and filter are - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const msearch: any = query.buildMSearch('1234'); - expect(msearch[0].index).toBe(legacyEventIndexPattern); - expect(msearch[1].query.bool.filter[1].bool.should[1].bool.filter[1]).toStrictEqual({ - terms: { 'endgame.data.alert_details.acting_process.unique_pid': ['1234'] }, - }); - }); - - it('constructs a non-legacy multi search query', () => { - const query = new StatsQuery('index-pattern'); - // using any here because otherwise ts complains that it doesn't know what bool and filter are - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const msearch: any = query.buildMSearch(['1234', '5678']); - expect(msearch[0].index).toBe('index-pattern'); - expect(msearch[1].query.bool.filter[0]).toStrictEqual({ - terms: { 'process.entity_id': ['1234', '5678'] }, - }); - }); -}); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/stats.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/stats.ts deleted file mode 100644 index 50e56258b7448..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/stats.ts +++ /dev/null @@ -1,246 +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; - * you may not use this file except in compliance with the Elastic License. - */ -import { SearchResponse } from 'elasticsearch'; -import { ResolverQuery } from './base'; -import { SafeResolverEvent, EventStats } from '../../../../../common/endpoint/types'; -import { JsonObject } from '../../../../../../../../src/plugins/kibana_utils/common'; - -export interface StatsResult { - alerts: Record; - events: Record; -} - -interface AggBucket { - key: string; - doc_count: number; -} - -interface CategoriesAgg extends AggBucket { - /** - * The reason categories is optional here is because if no data was returned in the query the categories aggregation - * will not be defined on the response (because it's a sub aggregation). - */ - categories?: { - buckets?: AggBucket[]; - }; -} - -export class StatsQuery extends ResolverQuery { - protected legacyQuery(endpointID: string, uniquePIDs: string[]): JsonObject { - return { - size: 0, - query: { - bool: { - filter: [ - { - term: { 'agent.id': endpointID }, - }, - { - bool: { - should: [ - { - bool: { - filter: [ - { term: { 'event.kind': 'event' } }, - { terms: { 'endgame.unique_pid': uniquePIDs } }, - { - bool: { - must_not: { - term: { 'event.category': 'process' }, - }, - }, - }, - ], - }, - }, - { - bool: { - filter: [ - { term: { 'event.kind': 'alert' } }, - { - terms: { - 'endgame.data.alert_details.acting_process.unique_pid': uniquePIDs, - }, - }, - ], - }, - }, - ], - }, - }, - ], - }, - }, - aggs: { - alerts: { - filter: { term: { 'event.kind': 'alert' } }, - aggs: { - ids: { - terms: { - field: 'endgame.data.alert_details.acting_process.unique_pid', - size: uniquePIDs.length, - }, - }, - }, - }, - events: { - filter: { term: { 'event.kind': 'event' } }, - aggs: { - ids: { - terms: { field: 'endgame.unique_pid', size: uniquePIDs.length }, - aggs: { - categories: { - terms: { field: 'event.category', size: 1000 }, - }, - }, - }, - }, - }, - }, - }; - } - - protected query(entityIDs: string[]): JsonObject { - return { - size: 0, - query: { - bool: { - filter: [ - { terms: { 'process.entity_id': entityIDs } }, - { - bool: { - should: [ - { - bool: { - filter: [ - { term: { 'event.kind': 'event' } }, - { - bool: { - must_not: { - term: { 'event.category': 'process' }, - }, - }, - }, - ], - }, - }, - { term: { 'event.kind': 'alert' } }, - ], - }, - }, - ], - }, - }, - aggs: { - alerts: { - filter: { term: { 'event.kind': 'alert' } }, - aggs: { - ids: { terms: { field: 'process.entity_id', size: entityIDs.length } }, - }, - }, - events: { - filter: { term: { 'event.kind': 'event' } }, - aggs: { - ids: { - // The entityIDs array will be made up of alert and event entity_ids, so we're guaranteed that there - // won't be anymore unique process.entity_ids than the size of the array passed in - terms: { field: 'process.entity_id', size: entityIDs.length }, - aggs: { - categories: { - // Currently ECS defines a small number of valid categories (under 10 right now), as ECS grows it's possible that the - // valid categories could exceed this hardcoded limit. If that happens we might want to revisit this - // and transition it to a composite aggregation so that we can paginate through all the possible response - terms: { field: 'event.category', size: 1000 }, - }, - }, - }, - }, - }, - }, - }; - } - - private static getEventStats(catAgg: CategoriesAgg): EventStats { - const total = catAgg.doc_count; - if (!catAgg.categories?.buckets) { - return { - total, - byCategory: {}, - }; - } - - const byCategory: Record = catAgg.categories.buckets.reduce( - (cummulative: Record, bucket: AggBucket) => ({ - ...cummulative, - [bucket.key]: bucket.doc_count, - }), - {} - ); - return { - total, - byCategory, - }; - } - - public formatResponse(response: SearchResponse): StatsResult { - let alerts: Record = {}; - - if (response.aggregations?.alerts?.ids?.buckets) { - alerts = response.aggregations.alerts.ids.buckets.reduce( - (cummulative: Record, bucket: AggBucket) => ({ - ...cummulative, - [bucket.key]: bucket.doc_count, - }), - {} - ); - } - - /** - * The response for the events ids aggregation should look like this: - * "aggregations" : { - * "ids" : { - * "doc_count_error_upper_bound" : 0, - * "sum_other_doc_count" : 0, - * "buckets" : [ - * { - * "key" : "entity_id1", - * "doc_count" : 3, - * "categories" : { - * "doc_count_error_upper_bound" : 0, - * "sum_other_doc_count" : 0, - * "buckets" : [ - * { - * "key" : "session", - * "doc_count" : 3 - * }, - * { - * "key" : "authentication", - * "doc_count" : 2 - * } - * ] - * } - * }, - * - * Which would indicate that entity_id1 had 3 related events. 3 of the related events had category session, - * and 2 had authentication - */ - let events: Record = {}; - if (response.aggregations?.events?.ids?.buckets) { - events = response.aggregations.events.ids.buckets.reduce( - (cummulative: Record, bucket: CategoriesAgg) => ({ - ...cummulative, - [bucket.key]: StatsQuery.getEventStats(bucket), - }), - {} - ); - } - - return { - alerts, - events, - }; - } -} diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree.ts deleted file mode 100644 index 08cb9b56bf64c..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree.ts +++ /dev/null @@ -1,43 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { RequestHandler, Logger } from 'kibana/server'; -import { TypeOf } from '@kbn/config-schema'; -import { eventsIndexPattern, alertsIndexPattern } from '../../../../common/endpoint/constants'; -import { validateTreeEntityID } from '../../../../common/endpoint/schema/resolver'; -import { Fetcher } from './utils/fetch'; -import { EndpointAppContext } from '../../types'; - -export function handleTree( - log: Logger, - endpointAppContext: EndpointAppContext -): RequestHandler< - TypeOf, - TypeOf -> { - return async (context, req, res) => { - try { - const client = context.core.elasticsearch.legacy.client; - - const fetcher = new Fetcher( - client, - req.params.id, - eventsIndexPattern, - alertsIndexPattern, - req.query.legacyEndpointID - ); - - const tree = await fetcher.tree(req.query); - - return res.ok({ - body: tree.render(), - }); - } catch (err) { - log.warn(err); - return res.internalError({ body: 'Error retrieving tree.' }); - } - }; -} diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/queries/descendants.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/queries/descendants.ts index 63cd3b5d694af..4f1c13ce1e619 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/queries/descendants.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/queries/descendants.ts @@ -8,7 +8,7 @@ import { ApiResponse } from '@elastic/elasticsearch'; import { IScopedClusterClient } from 'src/core/server'; import { FieldsObject, ResolverSchema } from '../../../../../../common/endpoint/types'; import { JsonObject, JsonValue } from '../../../../../../../../../src/plugins/kibana_utils/common'; -import { NodeID, TimeRange, docValueFields } from '../utils/index'; +import { NodeID, TimeRange, docValueFields, validIDs } from '../utils/index'; interface DescendantsParams { schema: ResolverSchema; @@ -65,6 +65,13 @@ export class DescendantsQuery { field: this.schema.parent, }, }, + { + bool: { + must_not: { + term: { [this.schema.id]: '' }, + }, + }, + }, { term: { 'event.category': 'process' }, }, @@ -152,6 +159,13 @@ export class DescendantsQuery { field: ancestryField, }, }, + { + bool: { + must_not: { + term: { [this.schema.id]: '' }, + }, + }, + }, { term: { 'event.category': 'process' }, }, @@ -176,19 +190,21 @@ export class DescendantsQuery { nodes: NodeID[], limit: number ): Promise { - if (nodes.length <= 0) { + const validNodes = validIDs(nodes); + + if (validNodes.length <= 0) { return []; } let response: ApiResponse>; if (this.schema.ancestry) { response = await client.asCurrentUser.search({ - body: this.queryWithAncestryArray(nodes, this.schema.ancestry, limit), + body: this.queryWithAncestryArray(validNodes, this.schema.ancestry, limit), index: this.indexPatterns, }); } else { response = await client.asCurrentUser.search({ - body: this.query(nodes, limit), + body: this.query(validNodes, limit), index: this.indexPatterns, }); } diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/queries/lifecycle.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/queries/lifecycle.ts index 150b07c63ce2f..4c75a075d13fc 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/queries/lifecycle.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/queries/lifecycle.ts @@ -8,7 +8,7 @@ import { ApiResponse } from '@elastic/elasticsearch'; import { IScopedClusterClient } from 'src/core/server'; import { FieldsObject, ResolverSchema } from '../../../../../../common/endpoint/types'; import { JsonObject, JsonValue } from '../../../../../../../../../src/plugins/kibana_utils/common'; -import { NodeID, TimeRange, docValueFields } from '../utils/index'; +import { NodeID, TimeRange, docValueFields, validIDs } from '../utils/index'; interface LifecycleParams { schema: ResolverSchema; @@ -60,6 +60,13 @@ export class LifecycleQuery { field: this.schema.id, }, }, + { + bool: { + must_not: { + term: { [this.schema.id]: '' }, + }, + }, + }, { term: { 'event.category': 'process' }, }, @@ -79,12 +86,13 @@ export class LifecycleQuery { * @param nodes the unique IDs to search for in Elasticsearch */ async search(client: IScopedClusterClient, nodes: NodeID[]): Promise { - if (nodes.length <= 0) { + const validNodes = validIDs(nodes); + if (validNodes.length <= 0) { return []; } const response: ApiResponse> = await client.asCurrentUser.search({ - body: this.query(nodes), + body: this.query(validNodes), index: this.indexPatterns, }); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/legacy_event_index_pattern.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/utils/index.test.ts similarity index 56% rename from x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/legacy_event_index_pattern.ts rename to x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/utils/index.test.ts index 01e818c89b3ef..2b8a7e4b2f7ee 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/legacy_event_index_pattern.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/utils/index.test.ts @@ -4,7 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -/** - * Legacy events are stored in indices with endgame-* prefix - */ -export const legacyEventIndexPattern = 'endgame-*'; +import { validIDs } from './index'; + +describe('validIDs', () => { + it('removes empty strings', () => { + expect(validIDs(['', 5, 'hello', '', 0])).toEqual([5, 'hello', 0]); + }); +}); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/utils/index.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/utils/index.ts index c00e90a386fb6..daa1edf3eabba 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/utils/index.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/utils/index.ts @@ -39,3 +39,12 @@ export function docValueFields(schema: ResolverSchema): Array<{ field: string }> } return filter; } + +/** + * Returns valid IDs that can be used in a search. + * + * @param ids array of ids + */ +export function validIDs(ids: NodeID[]): NodeID[] { + return ids.filter((id) => String(id) !== ''); +} diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/alerts_query_handler.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/alerts_query_handler.ts deleted file mode 100644 index f34218ddbde9b..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/alerts_query_handler.ts +++ /dev/null @@ -1,95 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { SearchResponse } from 'elasticsearch'; -import { ILegacyScopedClusterClient } from 'kibana/server'; -import { ResolverRelatedAlerts, SafeResolverEvent } from '../../../../../common/endpoint/types'; -import { createRelatedAlerts } from './node'; -import { AlertsQuery } from '../queries/alerts'; -import { PaginationBuilder } from './pagination'; -import { QueryInfo } from '../queries/multi_searcher'; -import { SingleQueryHandler } from './fetch'; - -/** - * Parameters for RelatedAlertsQueryHandler - */ -export interface RelatedAlertsParams { - limit: number; - entityID: string; - indexPattern: string; - after?: string; - legacyEndpointID?: string; - filter?: string; -} - -/** - * Requests related alerts for the given node. - */ -export class RelatedAlertsQueryHandler implements SingleQueryHandler { - private relatedAlerts: ResolverRelatedAlerts | undefined; - private readonly query: AlertsQuery; - private readonly limit: number; - private readonly entityID: string; - - constructor(options: RelatedAlertsParams) { - this.limit = options.limit; - this.entityID = options.entityID; - this.query = new AlertsQuery( - PaginationBuilder.createBuilder(this.limit, options.after), - options.indexPattern, - options.legacyEndpointID, - options.filter - ); - } - - private handleResponse = (response: SearchResponse) => { - const results = this.query.formatResponse(response); - this.relatedAlerts = createRelatedAlerts( - this.entityID, - results, - PaginationBuilder.buildCursorRequestLimit(this.limit, results) - ); - }; - - /** - * Builds a QueryInfo object that defines the related alerts to search for and how to handle the response. - * - * This will return undefined onces the results have been retrieved from ES. - */ - nextQuery(): QueryInfo | undefined { - if (this.getResults()) { - return; - } - - return { - query: this.query, - ids: this.entityID, - handler: this.handleResponse, - }; - } - - /** - * Get the results after an msearch. - */ - getResults() { - return this.relatedAlerts; - } - - /** - * Perform a regular search and return the results. - * - * @param client the elasticsearch client - */ - async search(client: ILegacyScopedClusterClient) { - const results = this.getResults(); - if (results) { - return results; - } - - this.handleResponse(await this.query.search(client, this.entityID)); - return this.getResults() ?? createRelatedAlerts(this.entityID); - } -} diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/ancestry_query_handler.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/ancestry_query_handler.ts deleted file mode 100644 index 00aab683bf010..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/ancestry_query_handler.ts +++ /dev/null @@ -1,158 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { SearchResponse } from 'elasticsearch'; -import { ILegacyScopedClusterClient } from 'kibana/server'; -import { - parentEntityIDSafeVersion, - entityIDSafeVersion, - ancestry, -} from '../../../../../common/endpoint/models/event'; -import { - SafeResolverAncestry, - SafeResolverEvent, - SafeResolverLifecycleNode, -} from '../../../../../common/endpoint/types'; -import { createAncestry, createLifecycle } from './node'; -import { LifecycleQuery } from '../queries/lifecycle'; -import { QueryInfo } from '../queries/multi_searcher'; -import { QueryHandler } from './fetch'; - -/** - * Retrieve the ancestry portion of a resolver tree. - */ -export class AncestryQueryHandler implements QueryHandler { - private readonly ancestry: SafeResolverAncestry = createAncestry(); - private ancestorsToFind: string[]; - private readonly query: LifecycleQuery; - - constructor( - private levels: number, - indexPattern: string, - legacyEndpointID: string | undefined, - originNode: SafeResolverLifecycleNode | undefined - ) { - const event = originNode?.lifecycle[0]; - this.ancestorsToFind = (event ? ancestry(event) : []).slice(0, levels); - this.query = new LifecycleQuery(indexPattern, legacyEndpointID); - - // add the origin node to the response if it exists - if (originNode) { - this.ancestry.ancestors.push(originNode); - this.ancestry.nextAncestor = parentEntityIDSafeVersion(originNode.lifecycle[0]) || null; - } - } - - private toMapOfNodes(results: SafeResolverEvent[]) { - return results.reduce( - (nodes: Map, event: SafeResolverEvent) => { - const nodeID = entityIDSafeVersion(event); - if (!nodeID) { - return nodes; - } - - let node = nodes.get(nodeID); - if (!node) { - node = createLifecycle(nodeID, []); - } - - node.lifecycle.push(event); - return nodes.set(nodeID, node); - }, - new Map() - ); - } - - private setNoMore() { - this.ancestry.nextAncestor = null; - this.ancestorsToFind = []; - this.levels = 0; - } - - private handleResponse = (searchResp: SearchResponse) => { - const results = this.query.formatResponse(searchResp); - if (results.length === 0) { - this.setNoMore(); - return; - } - - // bucket the start and end events together for a single node - const ancestryNodes = this.toMapOfNodes(results); - - /** - * This array (this.ancestry.ancestors) is the accumulated ancestors of the node of interest. This array is different - * from the ancestry array of a specific document. The order of this array is going to be weird, it will look like this - * [most distant ancestor...closer ancestor, next recursive call most distant ancestor...closer ancestor] - * - * Here is an example of why this happens - * Consider the following tree: - * A -> B -> C -> D -> E -> Origin - * Where A was spawn before B, which was before C, etc - * - * Let's assume the ancestry array limit is 2 so Origin's array would be: [E, D] - * E's ancestry array would be: [D, C] etc - * - * If a request comes in to retrieve all the ancestors in this tree, the accumulate results will be: - * [D, E, B, C, A] - * - * The first iteration would retrieve D and E in that order because they are sorted in ascending order by timestamp. - * The next iteration would get the ancestors of D (since that's the most distant ancestor from Origin) which are - * [B, C] - * The next iteration would get the ancestors of B which is A - * Hence: [D, E, B, C, A] - */ - this.ancestry.ancestors.push(...ancestryNodes.values()); - this.ancestry.nextAncestor = parentEntityIDSafeVersion(results[0]) || null; - this.levels = this.levels - ancestryNodes.size; - // the results come back in ascending order on timestamp so the first entry in the - // results should be the further ancestor (most distant grandparent) - this.ancestorsToFind = ancestry(results[0]).slice(0, this.levels); - }; - - /** - * Returns whether there are more results to retrieve based on the limit that is passed in and the results that - * have already been received from ES. - */ - hasMore(): boolean { - return this.levels > 0 && this.ancestorsToFind.length > 0; - } - - /** - * Get a query info for retrieving the next set of results. - */ - nextQuery(): QueryInfo | undefined { - if (this.hasMore()) { - return { - query: this.query, - ids: this.ancestorsToFind, - handler: this.handleResponse, - }; - } - } - - /** - * Return the results after using msearch to find them. - */ - getResults() { - return this.ancestry; - } - - /** - * Perform a regular search and return the results. - * - * @param client the elasticsearch client. - */ - async search(client: ILegacyScopedClusterClient) { - while (this.hasMore()) { - const info = this.nextQuery(); - if (!info) { - break; - } - this.handleResponse(await this.query.search(client, info.ids)); - } - return this.getResults(); - } -} diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_helper.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_helper.test.ts deleted file mode 100644 index d33e9a2d70af6..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_helper.test.ts +++ /dev/null @@ -1,202 +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; - * you may not use this file except in compliance with the Elastic License. - */ -import { - EndpointDocGenerator, - Tree, - Event, - TreeNode, -} from '../../../../../common/endpoint/generate_data'; -import { ChildrenNodesHelper } from './children_helper'; -import { eventIDSafeVersion, isProcessRunning } from '../../../../../common/endpoint/models/event'; - -function getStartEvents(events: Event[]): Event[] { - const startEvents: Event[] = []; - for (const event of events) { - if (isProcessRunning(event)) { - startEvents.push(event); - } - } - return startEvents; -} - -function getAllChildrenEvents(tree: Tree) { - const children: Event[] = []; - for (const child of tree.children.values()) { - children.push(...child.lifecycle); - } - return children; -} - -function getStartEventsFromLevels(levels: Array>) { - const startEvents: Event[] = []; - for (const level of levels) { - for (const node of level.values()) { - startEvents.push(...getStartEvents(node.lifecycle)); - } - } - - return startEvents; -} - -describe('Children helper', () => { - const generator = new EndpointDocGenerator(); - - let tree: Tree; - let helper: ChildrenNodesHelper; - let childrenEvents: Event[]; - let childrenStartEvents: Event[]; - beforeEach(() => { - tree = generator.generateTree({ - children: 3, - alwaysGenMaxChildrenPerNode: true, - generations: 3, - percentTerminated: 100, - ancestryArraySize: 2, - }); - helper = new ChildrenNodesHelper(tree.origin.id, tree.children.size); - childrenEvents = getAllChildrenEvents(tree); - childrenStartEvents = getStartEvents(childrenEvents); - }); - - it('returns the correct entity_ids', () => { - helper.addLifecycleEvents(childrenEvents); - expect(helper.getEntityIDs()).toEqual(Array.from(tree.children.keys())); - }); - - it('returns the correct number of nodes', () => { - helper.addLifecycleEvents(childrenEvents); - expect(helper.getNumNodes()).toEqual(tree.children.size); - }); - - it('marks the query nodes as null', () => { - // +1 indicates that we haven't received all the results so it should create a pagination cursor for the - // queried node (aka the origin that we're passing in) - helper = new ChildrenNodesHelper(tree.origin.id, tree.children.size + 1); - - const nextQuery = helper.addStartEvents(new Set([tree.origin.id]), childrenStartEvents); - helper.addStartEvents(nextQuery!, []); - const nodes = helper.getNodes(); - expect(nodes.nextChild).toBeNull(); - for (const node of nodes.childNodes) { - expect(node.nextChild).toBeNull(); - } - }); - - it('returns undefined when the limit is reached', () => { - helper = new ChildrenNodesHelper(tree.origin.id, tree.children.size - 1); - - expect(helper.addStartEvents(new Set([tree.origin.id]), childrenStartEvents)).toBeUndefined(); - }); - - it('handles multiple additions of start events', () => { - // + 1 indicates that we got everything that ES had - helper = new ChildrenNodesHelper(tree.origin.id, childrenStartEvents.length + 1); - - const level1And2 = getStartEventsFromLevels(tree.childrenLevels.slice(0, 2)); - let nextQuery = helper.addStartEvents(new Set([tree.origin.id]), level1And2); - expect(nextQuery?.size).toEqual(tree.childrenLevels[1].size); - for (const node of tree.childrenLevels[1].values()) { - expect(nextQuery?.has(node.id)).toBeTruthy(); - } - - const level3 = getStartEventsFromLevels(tree.childrenLevels.slice(2, 3)); - nextQuery = helper.addStartEvents(nextQuery!, level3); - expect(nextQuery).toBeUndefined(); - const nodes = helper.getNodes(); - expect(nodes.nextChild).toBeNull(); - for (const node of nodes.childNodes) { - expect(node.nextChild).toBeNull(); - } - }); - - it('handles an empty set', () => { - helper = new ChildrenNodesHelper(tree.origin.id, 1); - - const nextQuery = helper.addStartEvents(new Set([tree.origin.id]), []); - expect(nextQuery).toBeUndefined(); - const nodes = helper.getNodes(); - expect(nodes.nextChild).toBeNull(); - expect(nodes.childNodes.length).toEqual(0); - }); - - it('handles an empty set after multiple additions', () => { - // + 1 indicates that we got everything that ES had - helper = new ChildrenNodesHelper(tree.origin.id, childrenStartEvents.length + 1); - - const level1And2 = getStartEventsFromLevels(tree.childrenLevels.slice(0, 2)); - let nextQuery = helper.addStartEvents(new Set([tree.origin.id]), level1And2); - - nextQuery = helper.addStartEvents(nextQuery!, []); - expect(nextQuery).toBeUndefined(); - const nodes = helper.getNodes(); - expect(nodes.nextChild).toBeNull(); - for (const node of nodes.childNodes) { - expect(node.nextChild).toBeNull(); - } - }); - - it('non leaf nodes are set to undefined by default', () => { - // + 1 indicates that we got everything that ES had - helper = new ChildrenNodesHelper(tree.origin.id, childrenStartEvents.length + 1); - const level1And2 = getStartEventsFromLevels(tree.childrenLevels.slice(0, 2)); - helper.addStartEvents(new Set([tree.origin.id]), level1And2); - const nodes = helper.getNodes(); - expect(nodes.nextChild).toBeNull(); - for (const node of nodes.childNodes) { - if (tree.childrenLevels[0].has(node.entityID)) { - expect(node.nextChild).toBeNull(); - } else { - expect(node.nextChild).toBeUndefined(); - } - } - }); - - it('returns the leaf nodes', () => { - helper = new ChildrenNodesHelper(tree.origin.id, tree.children.size + 1); - - const nextQuery = helper.addStartEvents(new Set([tree.origin.id]), childrenStartEvents); - // we're using an ancestry array of 2 so the leaf nodes are at the second level - expect(nextQuery?.size).toEqual(tree.childrenLevels[1].size); - - for (const node of tree.childrenLevels[1].values()) { - expect(nextQuery?.has(node.id)).toBeTruthy(); - } - }); - - it('builds the children response structure', () => { - helper.addStartEvents(new Set([tree.origin.id]), childrenStartEvents); - helper.addLifecycleEvents(childrenEvents); - const childrenNodes = helper.getNodes(); - - // since we got all the nodes all the nextChild cursors should be null - for (const node of childrenNodes.childNodes) { - expect(node.nextChild).toBeUndefined(); - } - expect(childrenNodes.nextChild).not.toBeNull(); - - childrenNodes.childNodes.forEach((node) => { - node.lifecycle.forEach((event) => { - expect( - childrenEvents.find((child) => eventIDSafeVersion(child) === eventIDSafeVersion(event)) - ).toEqual(event); - }); - }); - }); - - it('builds the children response structure twice', () => { - helper.addLifecycleEvents(childrenEvents); - helper.getNodes(); - - const childrenNodes = helper.getNodes(); - childrenNodes.childNodes.forEach((node) => { - node.lifecycle.forEach((event) => { - expect( - childrenEvents.find((child) => eventIDSafeVersion(child) === eventIDSafeVersion(event)) - ).toEqual(event); - }); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_helper.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_helper.ts deleted file mode 100644 index 1a871891b1ed5..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_helper.ts +++ /dev/null @@ -1,174 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import * as eventModel from '../../../../../common/endpoint/models/event'; -import { - SafeResolverChildren, - SafeResolverChildNode, - SafeResolverEvent, -} from '../../../../../common/endpoint/types'; -import { createChild } from './node'; -import { ChildrenPaginationBuilder } from './children_pagination'; -import { ChildEvent } from '../queries/children'; - -/** - * This class helps construct the children structure when building a resolver tree. - */ -export class ChildrenNodesHelper { - private readonly entityToNodeCache: Map = new Map(); - - constructor(private readonly rootID: string, private readonly limit: number) { - this.entityToNodeCache.set(rootID, createChild(rootID)); - } - - /** - * Constructs a ResolverChildren response based on the children that were previously add. - */ - getNodes(): SafeResolverChildren { - const cacheCopy: Map = new Map(this.entityToNodeCache); - const rootNode = cacheCopy.get(this.rootID); - let rootNextChild = null; - - if (rootNode) { - rootNextChild = rootNode.nextChild ?? null; - } - - cacheCopy.delete(this.rootID); - return { - childNodes: Array.from(cacheCopy.values()), - nextChild: rootNextChild, - }; - } - - /** - * Get the entity_ids of the nodes that are cached. - */ - getEntityIDs(): string[] { - const cacheCopy: Map = new Map(this.entityToNodeCache); - cacheCopy.delete(this.rootID); - return Array.from(cacheCopy.keys()); - } - - /** - * Get the number of nodes that have been cached. - */ - getNumNodes(): number { - // -1 because the root node is in the cache too - return this.entityToNodeCache.size - 1; - } - - /** - * Add lifecycle events (start, end, etc) to the cache. - * - * @param lifecycle an array of resolver lifecycle events for different process nodes returned from ES. - */ - addLifecycleEvents(lifecycle: SafeResolverEvent[]) { - for (const event of lifecycle) { - const entityID = eventModel.entityIDSafeVersion(event); - if (entityID) { - const cachedChild = this.getOrCreateChildNode(entityID); - cachedChild.lifecycle.push(event); - } - } - } - - /** - * Add the start events for the nodes received from ES. Pagination cursors will be constructed based on the - * request limit and results returned. - * - * @param queriedNodes the entity_ids of the nodes that returned these start events - * @param startEvents an array of start events returned by ES - */ - addStartEvents(queriedNodes: Set, startEvents: ChildEvent[]): Set | undefined { - let largestAncestryArray = 0; - const nodesToQueryNext: Map> = new Map(); - const nonLeafNodes: Set = new Set(); - - const isDistantGrandchild = (event: ChildEvent) => { - const ancestry = eventModel.ancestry(event); - return ancestry.length > 0 && queriedNodes.has(ancestry[ancestry.length - 1]); - }; - - for (const event of startEvents) { - const parentID = eventModel.parentEntityIDSafeVersion(event); - const entityID = eventModel.entityIDSafeVersion(event); - if (parentID && entityID && eventModel.isProcessRunning(event)) { - // don't actually add the start event to the node, because that'll be done in - // a different call - const childNode = this.getOrCreateChildNode(entityID); - - const ancestry = eventModel.ancestry(event); - // This is to handle the following unlikely but possible scenario: - // if an alert was generated by the kernel process (parent process of all other processes) then - // the direct children of that process would only have an ancestry array of [parent_kernel], a single value in the array. - // The children of those children would have two values in their array [direct parent, parent_kernel] - // we need to determine which nodes are the most distant grandchildren of the queriedNodes because those should - // be used for the next query if more nodes should be retrieved. To generally determine the most distant grandchildren - // we can use the last entry in the ancestry array because of its ordering. The problem with that is in the scenario above - // the direct children of parent_kernel will also meet that criteria even though they are not actually the most - // distant grandchildren. To get around that issue we'll bucket all the nodes by the size of their ancestry array - // and then only return the nodes in the largest bucket because those should be the most distant grandchildren - // from the queried nodes that were passed in. - if (ancestry.length > largestAncestryArray) { - largestAncestryArray = ancestry.length; - } - - // a grandchild must have an array of > 0 and have it's last parent be in the set of previously queried nodes - // this is one of the furthest descendants from the queried nodes - if (isDistantGrandchild(event)) { - let levelOfNodes = nodesToQueryNext.get(ancestry.length); - if (!levelOfNodes) { - levelOfNodes = new Set(); - nodesToQueryNext.set(ancestry.length, levelOfNodes); - } - levelOfNodes.add(entityID); - } else { - nonLeafNodes.add(childNode); - } - } - } - - // we may not have received all the possible nodes so mark pagination for the query nodes - // we won't know if the non leaf nodes (non query nodes) have additional children so don't mark them - if (this.limit <= this.getNumNodes()) { - this.setPaginationForNodes(queriedNodes, startEvents); - return; - } - - // the non leaf nodes have received all their children so mark them as finished - for (const nonLeaf of nonLeafNodes.values()) { - nonLeaf.nextChild = null; - } - - // we've received all the descendants of the previously queried node that we can get using it's ancestry array - // so mark those nodes as complete - for (const nodeEntityID of queriedNodes.values()) { - const node = this.entityToNodeCache.get(nodeEntityID); - if (node) { - node.nextChild = null; - } - } - return nodesToQueryNext.get(largestAncestryArray); - } - - private setPaginationForNodes(nodes: Set, startEvents: ChildEvent[]) { - for (const nodeEntityID of nodes.values()) { - const cachedNode = this.entityToNodeCache.get(nodeEntityID); - if (cachedNode) { - cachedNode.nextChild = ChildrenPaginationBuilder.buildCursor(startEvents); - } - } - } - - private getOrCreateChildNode(entityID: string) { - let cachedChild = this.entityToNodeCache.get(entityID); - if (!cachedChild) { - cachedChild = createChild(entityID); - this.entityToNodeCache.set(entityID, cachedChild); - } - return cachedChild; - } -} diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_lifecycle_query_handler.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_lifecycle_query_handler.ts deleted file mode 100644 index f9f73c2ad75ff..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_lifecycle_query_handler.ts +++ /dev/null @@ -1,71 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { SearchResponse } from 'elasticsearch'; -import { ILegacyScopedClusterClient } from 'kibana/server'; -import { SafeResolverEvent, SafeResolverChildren } from '../../../../../common/endpoint/types'; -import { LifecycleQuery } from '../queries/lifecycle'; -import { QueryInfo } from '../queries/multi_searcher'; -import { SingleQueryHandler } from './fetch'; -import { ChildrenNodesHelper } from './children_helper'; -import { createChildren } from './node'; - -/** - * Returns the children of a resolver tree. - */ -export class ChildrenLifecycleQueryHandler implements SingleQueryHandler { - private lifecycle: SafeResolverChildren | undefined; - private readonly query: LifecycleQuery; - constructor( - private readonly childrenHelper: ChildrenNodesHelper, - indexPattern: string, - legacyEndpointID: string | undefined - ) { - this.query = new LifecycleQuery(indexPattern, legacyEndpointID); - } - - private handleResponse = (response: SearchResponse) => { - this.childrenHelper.addLifecycleEvents(this.query.formatResponse(response)); - this.lifecycle = this.childrenHelper.getNodes(); - }; - - /** - * Get the query for msearch. Once the results are set this will return undefined. - */ - nextQuery(): QueryInfo | undefined { - if (this.getResults()) { - return; - } - - return { - query: this.query, - ids: this.childrenHelper.getEntityIDs(), - handler: this.handleResponse, - }; - } - - /** - * Return the results from the search. - */ - getResults(): SafeResolverChildren | undefined { - return this.lifecycle; - } - - /** - * Perform a regular search and return the results. - * - * @param client the elasticsearch client - */ - async search(client: ILegacyScopedClusterClient) { - const results = this.getResults(); - if (results) { - return results; - } - - this.handleResponse(await this.query.search(client, this.childrenHelper.getEntityIDs())); - return this.getResults() ?? createChildren(); - } -} diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_pagination.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_pagination.ts deleted file mode 100644 index e202124554873..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_pagination.ts +++ /dev/null @@ -1,129 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { eventSequence, timestampSafeVersion } from '../../../../../common/endpoint/models/event'; -import { JsonObject } from '../../../../../../../../src/plugins/kibana_utils/common'; -import { urlEncodeCursor, SortFields, urlDecodeCursor } from './pagination'; -import { ChildEvent } from '../queries/children'; - -/** - * Pagination information for the children class. - */ -export interface ChildrenPaginationCursor { - timestamp: number; - sequence: number; -} - -/** - * Interface for defining the returned pagination information. - */ -export interface ChildrenPaginationFields { - sort: SortFields; - size: number; - filters: JsonObject[]; -} - -/** - * This class handles constructing pagination cursors that resolver can use to return additional events in subsequent - * queries. - */ -export class ChildrenPaginationBuilder { - constructor( - /** - * upper limit of how many results should be returned by the parent query. - */ - private readonly size: number, - /** - * timestamp that will be used in the search_after section - */ - private readonly timestamp?: number, - /** - * unique sequence number for the event - */ - private readonly sequence?: number - ) {} - - /** - * This function validates that the parsed cursor is a ChildrenPaginationCursor. - * - * @param parsed an object parsed from an encoded cursor. - */ - static decode( - parsed: ChildrenPaginationCursor | undefined - ): ChildrenPaginationCursor | undefined { - if (parsed && parsed.timestamp && parsed.sequence) { - const { timestamp, sequence } = parsed; - return { timestamp, sequence }; - } - } - - /** - * Construct a cursor to use in subsequent queries. - * - * @param results the events that were returned by the ES query - */ - static buildCursor(results: ChildEvent[]): string | null { - const lastResult = results[results.length - 1]; - const sequence = eventSequence(lastResult); - const cursor = { - timestamp: timestampSafeVersion(lastResult) ?? 0, - sequence: sequence === undefined ? 0 : sequence, - }; - return urlEncodeCursor(cursor); - } - - /** - * Creates a PaginationBuilder with an upper bound limit of results and a specific cursor to use to retrieve the next - * set of results. - * - * @param limit upper bound for the number of results to return within this query - * @param after a cursor to retrieve the next set of results - */ - static createBuilder(limit: number, after?: string): ChildrenPaginationBuilder { - if (after) { - try { - const cursor = urlDecodeCursor(after, ChildrenPaginationBuilder.decode); - if (cursor && cursor.timestamp && cursor.sequence) { - return new ChildrenPaginationBuilder(limit, cursor.timestamp, cursor.sequence); - } - } catch (err) { - /* tslint:disable:no-empty */ - } // ignore invalid cursor values - } - return new ChildrenPaginationBuilder(limit); - } - - /** - * Helper for creates an object for adding the pagination fields to a query - * - * @param tiebreaker a unique field to use as the tiebreaker for the search_after - * @returns an object containing the pagination information - */ - buildQueryFields(tiebreaker: string): ChildrenPaginationFields { - const sort: SortFields = [{ '@timestamp': 'asc' }, { [tiebreaker]: 'asc' }]; - const filters: JsonObject[] = []; - if (this.timestamp && this.sequence) { - filters.push( - { - range: { - '@timestamp': { - gte: this.timestamp, - }, - }, - }, - { - range: { - 'event.sequence': { - gt: this.sequence, - }, - }, - } - ); - } - - return { sort, size: this.size, filters }; - } -} diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_start_query_handler.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_start_query_handler.ts deleted file mode 100644 index 9f5f085d5b80d..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_start_query_handler.ts +++ /dev/null @@ -1,113 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { SearchResponse } from 'elasticsearch'; -import { ILegacyScopedClusterClient } from 'kibana/server'; -import { ChildEvent, ChildrenQuery } from '../queries/children'; -import { QueryInfo } from '../queries/multi_searcher'; -import { QueryHandler } from './fetch'; -import { ChildrenNodesHelper } from './children_helper'; -import { ChildrenPaginationBuilder } from './children_pagination'; - -/** - * Retrieve the start lifecycle events for the children of a resolver tree. - * - * If using msearch you should loop over hasMore() because the results are limited to the size of the ancestry array. - */ -export class ChildrenStartQueryHandler implements QueryHandler { - private readonly childrenHelper: ChildrenNodesHelper; - private limitLeft: number; - private query: ChildrenQuery; - private nodesToQuery: Set; - - constructor( - private readonly limit: number, - entityID: string, - after: string | undefined, - private readonly indexPattern: string, - private readonly legacyEndpointID: string | undefined - ) { - this.query = new ChildrenQuery( - ChildrenPaginationBuilder.createBuilder(limit, after), - indexPattern, - legacyEndpointID - ); - this.childrenHelper = new ChildrenNodesHelper(entityID, this.limit); - this.limitLeft = this.limit; - this.nodesToQuery = new Set([entityID]); - } - - private setNoMore() { - this.nodesToQuery = new Set(); - this.limitLeft = 0; - } - - private handleResponse = (response: SearchResponse) => { - const results = this.query.formatResponse(response); - this.nodesToQuery = this.childrenHelper.addStartEvents(this.nodesToQuery, results) ?? new Set(); - - if (results.length === 0) { - this.setNoMore(); - return; - } - - this.limitLeft = this.limit - this.childrenHelper.getNumNodes(); - - if (this.limitLeft < 0) { - this.limitLeft = 0; - } - - this.query = new ChildrenQuery( - ChildrenPaginationBuilder.createBuilder(this.limitLeft), - this.indexPattern, - this.legacyEndpointID - ); - }; - - /** - * Check if there are more results to retrieve based on the limit that was passed in. - */ - hasMore(): boolean { - return this.limitLeft > 0 && this.nodesToQuery.size > 0; - } - - /** - * Get a query to retrieve the next set of results. - */ - nextQuery(): QueryInfo | undefined { - if (this.hasMore()) { - return { - query: this.query, - // This should never be undefined because the check above - ids: Array.from(this.nodesToQuery.values()), - handler: this.handleResponse, - }; - } - } - - /** - * Get the cached results from the ES responses. - */ - getResults(): ChildrenNodesHelper { - return this.childrenHelper; - } - - /** - * Perform a regular search and return the helper. - * - * @param client the elasticsearch client - */ - async search(client: ILegacyScopedClusterClient) { - while (this.hasMore()) { - const info = this.nextQuery(); - if (!info) { - break; - } - this.handleResponse(await this.query.search(client, info.ids)); - } - return this.getResults(); - } -} diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/fetch.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/fetch.ts deleted file mode 100644 index 8f17a20e182ad..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/fetch.ts +++ /dev/null @@ -1,277 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { ILegacyScopedClusterClient } from 'kibana/server'; -import { - SafeResolverChildren, - SafeResolverAncestry, - ResolverRelatedAlerts, - SafeResolverLifecycleNode, -} from '../../../../../common/endpoint/types'; -import { Tree } from './tree'; -import { LifecycleQuery } from '../queries/lifecycle'; -import { StatsQuery } from '../queries/stats'; -import { createLifecycle } from './node'; -import { MultiSearcher, QueryInfo } from '../queries/multi_searcher'; -import { AncestryQueryHandler } from './ancestry_query_handler'; -import { RelatedAlertsQueryHandler } from './alerts_query_handler'; -import { ChildrenStartQueryHandler } from './children_start_query_handler'; -import { ChildrenLifecycleQueryHandler } from './children_lifecycle_query_handler'; -import { LifecycleQueryHandler } from './lifecycle_query_handler'; - -/** - * The query parameters passed in from the request. These define the limits for the ES requests for retrieving the - * resolver tree. - */ -export interface TreeOptions { - children: number; - ancestors: number; - events: number; - alerts: number; - afterAlert?: string; - afterEvent?: string; - afterChild?: string; -} - -interface QueryBuilder { - nextQuery(): QueryInfo | undefined; -} - -/** - * This interface defines the contract for a query handler that will only be used once in an msearch call. - */ -export interface SingleQueryHandler extends QueryBuilder { - /** - * This method returns the results if the query has been used in an msearch call or undefined if not. - */ - getResults(): T | undefined; - /** - * Do a regular search instead of msearch. - * @param client the elasticsearch client - */ - search(client: ILegacyScopedClusterClient): Promise; -} - -/** - * This interface defines the contract for a query handler that can be used multiple times by msearch. - */ -export interface QueryHandler extends SingleQueryHandler { - /** - * Returns whether additional msearch are required to retrieve the rest of the expected data from ES. - */ - hasMore(): boolean; -} - -/** - * Handles retrieving nodes of a resolver tree. - */ -export class Fetcher { - constructor( - private readonly client: ILegacyScopedClusterClient, - /** - * The anchoring origin for the tree. - */ - private readonly id: string, - /** - * Index pattern for searching ES for events - */ - private readonly eventsIndexPattern: string, - /** - * Index pattern for searching ES for alerts - */ - private readonly alertsIndexPattern: string, - /** - * This is used for searching legacy events - */ - private readonly endpointID?: string - ) {} - - /** - * This method retrieves the resolver tree starting from the `id` during construction of the class. - * - * @param options the options for retrieving the structure of the tree. - */ - public async tree(options: TreeOptions) { - const addQueryToList = (queryHandler: QueryBuilder, queries: QueryInfo[]) => { - const queryInfo = queryHandler.nextQuery(); - if (queryInfo !== undefined) { - queries.push(queryInfo); - } - }; - - const originHandler = new LifecycleQueryHandler( - this.id, - this.eventsIndexPattern, - this.endpointID - ); - - const alertsHandler = new RelatedAlertsQueryHandler({ - limit: options.alerts, - entityID: this.id, - after: options.afterAlert, - indexPattern: this.alertsIndexPattern, - legacyEndpointID: this.endpointID, - }); - - // we need to get the start events first because the API request defines how many nodes to return and we don't want - // to count or limit ourselves based on the other lifecycle events (end, etc) - const childrenHandler = new ChildrenStartQueryHandler( - options.children, - this.id, - options.afterChild, - this.eventsIndexPattern, - this.endpointID - ); - - const msearch = new MultiSearcher(this.client); - - let queries: QueryInfo[] = []; - addQueryToList(alertsHandler, queries); - addQueryToList(childrenHandler, queries); - addQueryToList(originHandler, queries); - - // get the related events, related alerts, the first pass of children start events, and the origin node - // the origin node is needed so we can get the ancestry array for the additional ancestor calls - await msearch.search(queries); - - const ancestryHandler = new AncestryQueryHandler( - options.ancestors, - this.eventsIndexPattern, - this.endpointID, - originHandler.getResults() - ); - - // get the remaining ancestors and children start events - while (ancestryHandler.hasMore() || childrenHandler.hasMore()) { - queries = []; - addQueryToList(ancestryHandler, queries); - addQueryToList(childrenHandler, queries); - await msearch.search(queries); - } - - const childrenTotalsHelper = childrenHandler.getResults(); - - const childrenLifecycleHandler = new ChildrenLifecycleQueryHandler( - childrenTotalsHelper, - this.eventsIndexPattern, - this.endpointID - ); - - // now that we have all the start events get the full lifecycle nodes - await childrenLifecycleHandler.search(this.client); - - const tree = new Tree(this.id, { - ancestry: ancestryHandler.getResults(), - relatedAlerts: alertsHandler.getResults(), - children: childrenLifecycleHandler.getResults(), - }); - - // add the stats to the tree - return this.stats(tree); - } - - /** - * Retrieves the ancestor nodes for the resolver tree. - * - * @param limit upper limit of ancestors to retrieve - */ - public async ancestors(limit: number): Promise { - const originNode = await this.getNode(this.id); - const ancestryHandler = new AncestryQueryHandler( - limit, - this.eventsIndexPattern, - this.endpointID, - originNode - ); - return ancestryHandler.search(this.client); - } - - /** - * Retrieves the children nodes for the resolver tree. - * - * @param limit the number of children to retrieve for a single level - * @param after a cursor to use as the starting point for retrieving children - */ - public async children(limit: number, after?: string): Promise { - const childrenHandler = new ChildrenStartQueryHandler( - limit, - this.id, - after, - this.eventsIndexPattern, - this.endpointID - ); - const helper = await childrenHandler.search(this.client); - const childrenLifecycleHandler = new ChildrenLifecycleQueryHandler( - helper, - this.eventsIndexPattern, - this.endpointID - ); - - return childrenLifecycleHandler.search(this.client); - } - - /** - * Retrieves the alerts for the origin node. - * - * @param limit the upper bound number of alerts to return. The limit is applied after the cursor is used to - * skip the previous results. - * @param after a cursor to use as the starting point for retrieving alerts - * @param filter a kql query string for filtering the results - */ - public async alerts( - limit: number, - after?: string, - filter?: string - ): Promise { - const alertsHandler = new RelatedAlertsQueryHandler({ - limit, - entityID: this.id, - after, - indexPattern: this.alertsIndexPattern, - legacyEndpointID: this.endpointID, - filter, - }); - - return alertsHandler.search(this.client); - } - - /** - * Enriches a resolver tree with statistics for how many related events and alerts exist for each node in the tree. - * - * @param tree a resolver tree to enrich with statistical information. - */ - public async stats(tree: Tree): Promise { - await this.doStats(tree); - return tree; - } - - private async getNode(entityID: string): Promise { - const query = new LifecycleQuery(this.eventsIndexPattern, this.endpointID); - const results = await query.searchAndFormat(this.client, entityID); - if (results.length === 0) { - return; - } - - return createLifecycle(entityID, results); - } - - private async doStats(tree: Tree) { - const statsQuery = new StatsQuery( - [this.eventsIndexPattern, this.alertsIndexPattern], - this.endpointID - ); - const ids = tree.ids(); - const res = await statsQuery.searchAndFormat(this.client, ids); - const alerts = res.alerts; - const events = res.events; - ids.forEach((id) => { - tree.addStats(id, { - totalAlerts: alerts[id] || 0, - events: events[id] || { total: 0, byCategory: {} }, - }); - }); - } -} diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/lifecycle_query_handler.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/lifecycle_query_handler.ts deleted file mode 100644 index d4dc12d5e8b66..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/lifecycle_query_handler.ts +++ /dev/null @@ -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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { SearchResponse } from 'elasticsearch'; -import { ILegacyScopedClusterClient } from 'kibana/server'; -import { SafeResolverEvent, SafeResolverLifecycleNode } from '../../../../../common/endpoint/types'; -import { LifecycleQuery } from '../queries/lifecycle'; -import { QueryInfo } from '../queries/multi_searcher'; -import { SingleQueryHandler } from './fetch'; -import { createLifecycle } from './node'; - -/** - * Retrieve the lifecycle events for a node. - */ -export class LifecycleQueryHandler implements SingleQueryHandler { - private lifecycle: SafeResolverLifecycleNode | undefined; - private readonly query: LifecycleQuery; - constructor( - private readonly entityID: string, - indexPattern: string, - legacyEndpointID: string | undefined - ) { - this.query = new LifecycleQuery(indexPattern, legacyEndpointID); - } - - private handleResponse = (response: SearchResponse) => { - const results = this.query.formatResponse(response); - if (results.length !== 0) { - this.lifecycle = createLifecycle(this.entityID, results); - } - }; - - /** - * Build the query for retrieving the lifecycle events. This will return undefined once the results have been found. - */ - nextQuery(): QueryInfo | undefined { - if (this.getResults()) { - return; - } - - return { - query: this.query, - ids: this.entityID, - handler: this.handleResponse, - }; - } - - /** - * Get the results from the msearch. - */ - getResults(): SafeResolverLifecycleNode | undefined { - return this.lifecycle; - } - - /** - * Do a regular search and return the results. - * - * @param client the elasticsearch client. - */ - async search(client: ILegacyScopedClusterClient) { - const results = this.getResults(); - if (results) { - return results; - } - - this.handleResponse(await this.query.search(client, this.entityID)); - return this.getResults() ?? createLifecycle(this.entityID, []); - } -} diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/node.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/node.ts deleted file mode 100644 index 286564d9302c1..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/node.ts +++ /dev/null @@ -1,120 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { - SafeResolverAncestry, - SafeResolverTree, - ResolverRelatedAlerts, - SafeResolverChildren, - SafeResolverLifecycleNode, - SafeResolverEvent, - SafeResolverChildNode, - ResolverPaginatedEvents, -} from '../../../../../common/endpoint/types'; - -/** - * Creates an object that the events handler would return - * - * @param events array of events - * @param nextEvent the cursor to retrieve the next event - */ -export function createEvents( - events: SafeResolverEvent[] = [], - nextEvent: string | null = null -): ResolverPaginatedEvents { - return { events, nextEvent }; -} - -/** - * Creates an alert object that the alerts handler would return - * - * @param entityID the entity_id for these related events - * @param alerts array of alerts - * @param nextAlert the cursor to retrieve the next alert - */ -export function createRelatedAlerts( - entityID: string, - alerts: SafeResolverEvent[] = [], - nextAlert: string | null = null -): ResolverRelatedAlerts { - return { entityID, alerts, nextAlert }; -} - -/** - * Creates a child node that would be used in the child handler response - * - * @param entityID the entity_id of the child - */ -export function createChild(entityID: string): SafeResolverChildNode { - const lifecycle = createLifecycle(entityID, []); - return { - ...lifecycle, - }; -} - -/** - * Creates an empty ancestry response structure. - */ -export function createAncestry(): SafeResolverAncestry { - return { ancestors: [], nextAncestor: null }; -} - -/** - * Creates a lifecycle node for use in the ancestry or child handlers - * - * @param id the entity_id that these lifecycle nodes should have - * @param lifecycle an array of lifecycle events - */ -export function createLifecycle( - entityID: string, - lifecycle: SafeResolverEvent[] -): SafeResolverLifecycleNode { - return { entityID, lifecycle }; -} - -/** - * Creates a resolver children response. - * - * @param nodes the child nodes to add to the ResolverChildren response - * @param nextChild the cursor for the response - */ -export function createChildren( - nodes: SafeResolverChildNode[] = [], - nextChild: string | null = null -): SafeResolverChildren { - return { childNodes: nodes, nextChild }; -} - -/** - * Creates an empty `Tree` response structure that the tree handler would return - * - * @param entityID the entity_id of the tree's origin node - */ -export function createTree(entityID: string): SafeResolverTree { - return { - entityID, - children: { - childNodes: [], - nextChild: null, - }, - relatedAlerts: { - alerts: [], - nextAlert: null, - }, - lifecycle: [], - ancestry: { - ancestors: [], - nextAncestor: null, - }, - stats: { - totalAlerts: 0, - events: { - total: 0, - byCategory: {}, - }, - }, - }; -} diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/pagination.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/pagination.ts index af0311a262f30..2334a59d6940f 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/pagination.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/pagination.ts @@ -10,7 +10,6 @@ import { timestampSafeVersion, } from '../../../../../common/endpoint/models/event'; import { JsonObject } from '../../../../../../../../src/plugins/kibana_utils/common'; -import { ChildrenPaginationCursor } from './children_pagination'; type SearchAfterFields = [number, string]; @@ -54,7 +53,7 @@ export interface PaginationFields { * * @param data Transforms a pagination cursor into a base64 encoded string */ -export function urlEncodeCursor(data: PaginationCursor | ChildrenPaginationCursor): string { +export function urlEncodeCursor(data: PaginationCursor): string { const value = JSON.stringify(data); return Buffer.from(value, 'utf8') .toString('base64') diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/tree.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/tree.test.ts deleted file mode 100644 index ce933380e9f34..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/tree.test.ts +++ /dev/null @@ -1,45 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { EndpointDocGenerator } from '../../../../../common/endpoint/generate_data'; -import { Tree } from './tree'; -import { SafeResolverAncestry, SafeResolverEvent } from '../../../../../common/endpoint/types'; -import { entityIDSafeVersion } from '../../../../../common/endpoint/models/event'; - -describe('Tree', () => { - const generator = new EndpointDocGenerator(); - - describe('ancestry', () => { - // transform the generator's array of events into the format expected by the tree class - const ancestorInfo: SafeResolverAncestry = { - ancestors: generator - .createAlertEventAncestry({ ancestors: 5, percentTerminated: 0, percentWithRelated: 0 }) - .filter((event) => { - return event.event?.kind === 'event'; - }) - .map((event) => { - return { - entityID: entityIDSafeVersion(event) ?? '', - // The generator returns Events, but the tree needs a ResolverEvent - lifecycle: [event as SafeResolverEvent], - }; - }), - nextAncestor: 'hello', - }; - - it('adds ancestors to the tree', () => { - const tree = new Tree(ancestorInfo.ancestors[0].entityID, { ancestry: ancestorInfo }); - const ids = tree.ids(); - ids.forEach((id) => { - const foundAncestor = ancestorInfo.ancestors.find( - (ancestor) => entityIDSafeVersion(ancestor.lifecycle[0]) === id - ); - expect(foundAncestor).not.toBeUndefined(); - }); - expect(tree.render().ancestry.nextAncestor).toEqual('hello'); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/tree.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/tree.ts deleted file mode 100644 index 26ac15e73759a..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/tree.ts +++ /dev/null @@ -1,129 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import _ from 'lodash'; -import { - SafeResolverEvent, - ResolverNodeStats, - SafeResolverAncestry, - SafeResolverTree, - SafeResolverChildren, - ResolverRelatedAlerts, -} from '../../../../../common/endpoint/types'; -import { createTree } from './node'; - -interface Node { - entityID: string; - lifecycle: SafeResolverEvent[]; - stats?: ResolverNodeStats; -} - -export interface Options { - ancestry?: SafeResolverAncestry; - children?: SafeResolverChildren; - relatedAlerts?: ResolverRelatedAlerts; -} - -/** - * This class aids in constructing a tree of process events. - * - * This Tree's root/origin will likely be in the middle of the tree. The origin corresponds to the id passed in when this - * Tree object is constructed. The tree can have ancestors and children coming from the origin. - */ -export class Tree { - protected cache: Map = new Map(); - protected tree: SafeResolverTree; - - constructor(protected readonly id: string, options: Options = {}) { - const tree = createTree(this.id); - this.tree = tree; - this.cache.set(id, tree); - - this.addAncestors(options.ancestry); - this.addChildren(options.children); - this.addRelatedAlerts(options.relatedAlerts); - } - - /** - * Return the origin node. The origin node is the node with the id that the tree was built using. - * - * @returns the origin ResolverNode - */ - public render(): SafeResolverTree { - return this.tree; - } - - /** - * Returns an array of all the unique IDs for the nodes stored in this tree. - * - * @returns an array of strings representing the unique IDs for the nodes in the tree - */ - public ids(): string[] { - return [...this.cache.keys()]; - } - - /** - * Add alerts for the tree's origin node. Alerts cannot be added for other nodes. - * - * @param alertInfo is the alerts and pagination information to add to the tree. - */ - private addRelatedAlerts(alertInfo: ResolverRelatedAlerts | undefined) { - if (!alertInfo) { - return; - } - - this.tree.relatedAlerts.alerts = alertInfo.alerts; - this.tree.relatedAlerts.nextAlert = alertInfo.nextAlert; - } - - /** - * Add ancestors to the tree. - * - * @param ancestorInfo is the ancestors and pagination information to add to the tree. - */ - private addAncestors(ancestorInfo: SafeResolverAncestry | undefined) { - if (!ancestorInfo) { - return; - } - - this.tree.ancestry.nextAncestor = ancestorInfo.nextAncestor; - - // the ancestry info holds the lifecycle events for the root of the tree too, so we need to pull that out - ancestorInfo.ancestors.forEach((node) => { - if (node.entityID === this.id) { - this.tree.lifecycle = node.lifecycle; - return; - } - this.cache.set(node.entityID, node); - this.tree.ancestry.ancestors.push(node); - }); - } - - /** - * Add statistics to a node. - * - * @param id unique node ID to add the stats information to - * @param stats information indicating how many related events, and alerts exist for the specific node. - */ - public addStats(id: string, stats: ResolverNodeStats) { - const currentNode = this.cache.get(id); - if (currentNode !== undefined) { - currentNode.stats = stats; - } - } - - private addChildren(children: SafeResolverChildren | undefined) { - if (!children) { - return; - } - - this.tree.children = children; - - children.childNodes.forEach((child) => { - this.cache.set(child.entityID, child); - }); - } -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/create_notifications.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/create_notifications.ts index 5731a51aeabc1..f0895f1367289 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/create_notifications.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/create_notifications.ts @@ -6,7 +6,7 @@ import { Alert } from '../../../../../alerts/common'; import { SERVER_APP_ID, NOTIFICATIONS_ID } from '../../../../common/constants'; -import { CreateNotificationParams } from './types'; +import { CreateNotificationParams, RuleNotificationAlertTypeParams } from './types'; import { addTags } from './add_tags'; import { transformRuleToAlertAction } from '../../../../common/detection_engine/transform_actions'; @@ -17,8 +17,8 @@ export const createNotifications = async ({ ruleAlertId, interval, name, -}: CreateNotificationParams): Promise => - alertsClient.create({ +}: CreateNotificationParams): Promise> => + alertsClient.create({ data: { name, tags: addTags([], ruleAlertId), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/find_notifications.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/find_notifications.ts index 5d3a328dd6fbb..1bf07a1574be6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/find_notifications.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/find_notifications.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { FindResult } from '../../../../../alerts/server'; +import { AlertTypeParams, FindResult } from '../../../../../alerts/server'; import { NOTIFICATIONS_ID } from '../../../../common/constants'; import { FindNotificationParams } from './types'; @@ -24,7 +24,7 @@ export const findNotifications = async ({ filter, sortField, sortOrder, -}: FindNotificationParams): Promise => +}: FindNotificationParams): Promise> => alertsClient.find({ options: { fields, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/read_notifications.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/read_notifications.ts index fe9101335b4f5..74df77bf9bf74 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/read_notifications.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/read_notifications.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SanitizedAlert } from '../../../../../alerts/common'; +import { AlertTypeParams, SanitizedAlert } from '../../../../../alerts/common'; import { ReadNotificationParams, isAlertType } from './types'; import { findNotifications } from './find_notifications'; import { INTERNAL_RULE_ALERT_ID_KEY } from '../../../../common/constants'; @@ -13,7 +13,7 @@ export const readNotifications = async ({ alertsClient, id, ruleAlertId, -}: ReadNotificationParams): Promise => { +}: ReadNotificationParams): Promise | null> => { if (id != null) { try { const notification = await alertsClient.get({ id }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/types.ts index cc9fb149a7e1b..e4e9df552101b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/types.ts @@ -8,18 +8,20 @@ import { AlertsClient, PartialAlert, AlertType, + AlertTypeParams, AlertTypeState, + AlertInstanceState, + AlertInstanceContext, AlertExecutorOptions, } from '../../../../../alerts/server'; import { Alert } from '../../../../../alerts/common'; import { NOTIFICATIONS_ID } from '../../../../common/constants'; import { RuleAlertAction } from '../../../../common/detection_engine/types'; -export interface RuleNotificationAlertType extends Alert { - params: { - ruleAlertId: string; - }; +export interface RuleNotificationAlertTypeParams extends AlertTypeParams { + ruleAlertId: string; } +export type RuleNotificationAlertType = Alert; export interface FindNotificationParams { alertsClient: AlertsClient; @@ -76,32 +78,36 @@ export interface ReadNotificationParams { } export const isAlertTypes = ( - partialAlert: PartialAlert[] + partialAlert: Array> ): partialAlert is RuleNotificationAlertType[] => { return partialAlert.every((rule) => isAlertType(rule)); }; export const isAlertType = ( - partialAlert: PartialAlert + partialAlert: PartialAlert ): partialAlert is RuleNotificationAlertType => { return partialAlert.alertTypeId === NOTIFICATIONS_ID; }; -export type NotificationExecutorOptions = Omit & { - params: { - ruleAlertId: string; - }; -}; +export type NotificationExecutorOptions = AlertExecutorOptions< + RuleNotificationAlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext +>; // This returns true because by default a NotificationAlertTypeDefinition is an AlertType // since we are only increasing the strictness of params. export const isNotificationAlertExecutor = ( obj: NotificationAlertTypeDefinition -): obj is AlertType => { +): obj is AlertType => { return true; }; -export type NotificationAlertTypeDefinition = Omit & { +export type NotificationAlertTypeDefinition = Omit< + AlertType, + 'executor' +> & { executor: ({ services, params, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/update_notifications.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/update_notifications.ts index d6c8973215117..8528d53b51f5b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/update_notifications.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/update_notifications.ts @@ -6,7 +6,7 @@ import { PartialAlert } from '../../../../../alerts/server'; import { readNotifications } from './read_notifications'; -import { UpdateNotificationParams } from './types'; +import { RuleNotificationAlertTypeParams, UpdateNotificationParams } from './types'; import { addTags } from './add_tags'; import { createNotifications } from './create_notifications'; import { transformRuleToAlertAction } from '../../../../common/detection_engine/transform_actions'; @@ -18,11 +18,11 @@ export const updateNotifications = async ({ ruleAlertId, name, interval, -}: UpdateNotificationParams): Promise => { +}: UpdateNotificationParams): Promise | null> => { const notification = await readNotifications({ alertsClient, id: undefined, ruleAlertId }); if (interval && notification) { - return alertsClient.update({ + return alertsClient.update({ id: notification.id, data: { tags: addTags([], ruleAlertId), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts index b185b8780abe2..3473948b000c7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts @@ -22,6 +22,8 @@ import { buildRouteValidation } from '../../../../utils/build_validation/route_v import { transformBulkError, createBulkErrorObject, buildSiemResponse } from '../utils'; import { updateRulesNotifications } from '../../rules/update_rules_notifications'; import { convertCreateAPIToInternalSchema } from '../../schemas/rule_converters'; +import { RuleTypeParams } from '../../types'; +import { Alert } from '../../../../../../alerts/common'; export const createRulesBulkRoute = (router: IRouter, ml: SetupPlugins['ml']) => { router.post( @@ -95,9 +97,12 @@ export const createRulesBulkRoute = (router: IRouter, ml: SetupPlugins['ml']) => }); } - const createdRule = await alertsClient.create({ + /** + * TODO: Remove this use of `as` by utilizing the proper type + */ + const createdRule = (await alertsClient.create({ data: internalRule, - }); + })) as Alert; const ruleActions = await updateRulesNotifications({ ruleAlertId: createdRule.id, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts index b52248f670188..c59d5d2a36fd5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts @@ -19,6 +19,8 @@ import { createRulesSchema } from '../../../../../common/detection_engine/schema import { newTransformValidate } from './validate'; import { createRuleValidateTypeDependents } from '../../../../../common/detection_engine/schemas/request/create_rules_type_dependents'; import { convertCreateAPIToInternalSchema } from '../../schemas/rule_converters'; +import { RuleTypeParams } from '../../types'; +import { Alert } from '../../../../../../alerts/common'; export const createRulesRoute = (router: IRouter, ml: SetupPlugins['ml']): void => { router.post( @@ -85,9 +87,12 @@ export const createRulesRoute = (router: IRouter, ml: SetupPlugins['ml']): void // This will create the endpoint list if it does not exist yet await context.lists?.getExceptionListClient().createEndpointList(); - const createdRule = await alertsClient.create({ + /** + * TODO: Remove this use of `as` by utilizing the proper type + */ + const createdRule = (await alertsClient.create({ data: internalRule, - }); + })) as Alert; const ruleActions = await updateRulesNotifications({ ruleAlertId: createdRule.id, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.ts index 7a6cd707eb185..bd7f11b2d8756 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.ts @@ -31,6 +31,7 @@ import { OutputError, } from '../utils'; import { RuleActions } from '../../rule_actions/types'; +import { RuleTypeParams } from '../../types'; type PromiseFromStreams = ImportRulesSchemaDecoded | Error; @@ -172,7 +173,7 @@ export const transformAlertsToRules = (alerts: RuleAlertType[]): Array, ruleActions: Array, ruleStatuses?: Array> ): { @@ -203,7 +204,7 @@ export const transformFindAlerts = ( }; export const transform = ( - alert: PartialAlert, + alert: PartialAlert, ruleActions?: RuleActions | null, ruleStatus?: SavedObject ): Partial | null => { @@ -220,7 +221,7 @@ export const transform = ( export const transformOrBulkError = ( ruleId: string, - alert: PartialAlert, + alert: PartialAlert, ruleActions: RuleActions, ruleStatus?: unknown ): Partial | BulkError => { @@ -241,7 +242,7 @@ export const transformOrBulkError = ( export const transformOrImportError = ( ruleId: string, - alert: PartialAlert, + alert: PartialAlert, existingImportSuccessError: ImportSuccessError ): ImportSuccessError => { if (isAlertType(alert)) { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.test.ts index 8653bdc0427e4..51b08cb3fce89 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.test.ts @@ -15,6 +15,7 @@ import { RulesSchema } from '../../../../../common/detection_engine/schemas/resp import { getResult, getFindResultStatus } from '../__mocks__/request_responses'; import { getListArrayMock } from '../../../../../common/detection_engine/schemas/types/lists.mock'; import { getThreatMock } from '../../../../../common/detection_engine/schemas/types/threat.mock'; +import { RuleTypeParams } from '../../types'; export const ruleOutput = (): RulesSchema => ({ actions: [], @@ -88,7 +89,7 @@ describe('validate', () => { describe('transformValidateFindAlerts', () => { test('it should do a validation correctly of a find alert', () => { - const findResult: FindResult = { + const findResult: FindResult = { data: [getResult()], page: 1, perPage: 0, @@ -111,7 +112,7 @@ describe('validate', () => { }); test('it should do an in-validation correctly of a partial alert', () => { - const findResult: FindResult = { + const findResult: FindResult = { data: [getResult()], page: 1, perPage: 0, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.ts index 382186df16cd1..3e3b85a407fc2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.ts @@ -31,9 +31,10 @@ import { import { createBulkErrorObject, BulkError } from '../utils'; import { transformFindAlerts, transform, transformAlertToRule } from './utils'; import { RuleActions } from '../../rule_actions/types'; +import { RuleTypeParams } from '../../types'; export const transformValidateFindAlerts = ( - findResults: FindResult, + findResults: FindResult, ruleActions: Array, ruleStatuses?: Array> ): [ @@ -63,7 +64,7 @@ export const transformValidateFindAlerts = ( }; export const transformValidate = ( - alert: PartialAlert, + alert: PartialAlert, ruleActions?: RuleActions | null, ruleStatus?: SavedObject ): [RulesSchema | null, string | null] => { @@ -76,7 +77,7 @@ export const transformValidate = ( }; export const newTransformValidate = ( - alert: PartialAlert, + alert: PartialAlert, ruleActions?: RuleActions | null, ruleStatus?: SavedObject ): [FullResponseSchema | null, string | null] => { @@ -90,7 +91,7 @@ export const newTransformValidate = ( export const transformValidateBulkError = ( ruleId: string, - alert: PartialAlert, + alert: PartialAlert, ruleActions?: RuleActions | null, ruleStatus?: SavedObjectsFindResponse ): RulesSchema | BulkError => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts index 0519a98df1fae..6339e92eb012b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts @@ -9,6 +9,7 @@ import { Alert } from '../../../../../alerts/common'; import { SERVER_APP_ID, SIGNALS_ID } from '../../../../common/constants'; import { CreateRulesOptions } from './types'; import { addTags } from './add_tags'; +import { PartialFilter, RuleTypeParams } from '../types'; export const createRules = async ({ alertsClient, @@ -59,8 +60,8 @@ export const createRules = async ({ version, exceptionsList, actions, -}: CreateRulesOptions): Promise => { - return alertsClient.create({ +}: CreateRulesOptions): Promise> => { + return alertsClient.create({ data: { name, tags: addTags(tags, ruleId, immutable), @@ -95,7 +96,10 @@ export const createRules = async ({ severityMapping, threat, threshold, - threatFilters, + /** + * TODO: Fix typing inconsistancy between `RuleTypeParams` and `CreateRulesOptions` + */ + threatFilters: threatFilters as PartialFilter[] | undefined, threatIndex, threatQuery, concurrentSearches, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/find_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/find_rules.ts index 18b851c440e20..5ab97262515da 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/find_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/find_rules.ts @@ -6,6 +6,7 @@ import { FindResult } from '../../../../../alerts/server'; import { SIGNALS_ID } from '../../../../common/constants'; +import { RuleTypeParams } from '../types'; import { FindRuleOptions } from './types'; export const getFilter = (filter: string | null | undefined) => { @@ -24,7 +25,7 @@ export const findRules = async ({ filter, sortField, sortOrder, -}: FindRuleOptions): Promise => { +}: FindRuleOptions): Promise> => { return alertsClient.find({ options: { fields, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/install_prepacked_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/install_prepacked_rules.ts index 4c01318f02cde..1f43706c17fec 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/install_prepacked_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/install_prepacked_rules.ts @@ -5,7 +5,7 @@ */ import { AddPrepackagedRulesSchemaDecoded } from '../../../../common/detection_engine/schemas/request/add_prepackaged_rules_schema'; -import { Alert } from '../../../../../alerts/common'; +import { Alert, AlertTypeParams } from '../../../../../alerts/common'; import { AlertsClient } from '../../../../../alerts/server'; import { createRules } from './create_rules'; import { PartialFilter } from '../types'; @@ -14,8 +14,8 @@ export const installPrepackagedRules = ( alertsClient: AlertsClient, rules: AddPrepackagedRulesSchemaDecoded[], outputIndex: string -): Array> => - rules.reduce>>((acc, rule) => { +): Array>> => + rules.reduce>>>((acc, rule) => { const { anomaly_threshold: anomalyThreshold, author, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.mock.ts index b2303d48b0517..484dac8e31fb4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.mock.ts @@ -9,8 +9,9 @@ import { alertsClientMock } from '../../../../../alerts/server/mocks'; import { savedObjectsClientMock } from '../../../../../../../src/core/server/mocks'; import { INTERNAL_RULE_ID_KEY, INTERNAL_IMMUTABLE_KEY } from '../../../../common/constants'; import { SanitizedAlert } from '../../../../../alerts/common'; +import { RuleTypeParams } from '../types'; -const rule: SanitizedAlert = { +const rule: SanitizedAlert = { id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', name: 'Detect Root/Admin Users', tags: [`${INTERNAL_RULE_ID_KEY}:rule-1`, `${INTERNAL_IMMUTABLE_KEY}:false`], @@ -67,6 +68,8 @@ const rule: SanitizedAlert = { note: '# Investigative notes', version: 1, exceptionsList: [ + /** + TODO: fix this mock. Which the typing has revealed is wrong { field: 'source.ip', values_operator: 'included', @@ -96,8 +99,31 @@ const rule: SanitizedAlert = { ], }, ], - }, + },*/ ], + /** + * The fields below were missing as the type was partial and hence not technically correct + */ + author: [], + buildingBlockType: undefined, + eventCategoryOverride: undefined, + license: undefined, + savedId: undefined, + interval: undefined, + riskScoreMapping: undefined, + ruleNameOverride: undefined, + name: undefined, + severityMapping: undefined, + tags: undefined, + threshold: undefined, + threatFilters: undefined, + threatIndex: undefined, + threatQuery: undefined, + threatMapping: undefined, + threatLanguage: undefined, + concurrentSearches: undefined, + itemsPerSearch: undefined, + timestampOverride: undefined, }, createdAt: new Date('2019-12-13T16:40:33.400Z'), updatedAt: new Date('2019-12-13T16:40:33.400Z'), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.ts index c86526cee9302..c1720c4fa3587 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.ts @@ -13,6 +13,7 @@ import { addTags } from './add_tags'; import { calculateVersion, calculateName, calculateInterval, removeUndefined } from './utils'; import { ruleStatusSavedObjectsClientFactory } from '../signals/rule_status_saved_objects_client'; import { internalRuleUpdate } from '../schemas/rule_schemas'; +import { RuleTypeParams } from '../types'; class PatchError extends Error { public readonly statusCode: number; @@ -71,7 +72,7 @@ export const patchRules = async ({ anomalyThreshold, machineLearningJobId, actions, -}: PatchRulesOptions): Promise => { +}: PatchRulesOptions): Promise | null> => { if (rule == null) { return null; } @@ -185,10 +186,13 @@ export const patchRules = async ({ throw new PatchError(`Applying patch would create invalid rule: ${errors}`, 400); } - const update = await alertsClient.update({ + /** + * TODO: Remove this use of `as` by utilizing the proper type + */ + const update = (await alertsClient.update({ id: rule.id, data: validated, - }); + })) as PartialAlert; if (rule.enabled && enabled === false) { await alertsClient.disable({ id: rule.id }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.ts index e4bb65a907e2d..bc277bd2089a6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.ts @@ -6,6 +6,7 @@ import { SanitizedAlert } from '../../../../../alerts/common'; import { INTERNAL_RULE_ID_KEY } from '../../../../common/constants'; +import { RuleTypeParams } from '../types'; import { findRules } from './find_rules'; import { isAlertType, ReadRuleOptions } from './types'; @@ -21,7 +22,7 @@ export const readRules = async ({ alertsClient, id, ruleId, -}: ReadRuleOptions): Promise => { +}: ReadRuleOptions): Promise | null> => { if (id != null) { try { const rule = await alertsClient.get({ id }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts index aaeb487ad9a78..34dab20c279b4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts @@ -103,9 +103,7 @@ import { SIGNALS_ID } from '../../../../common/constants'; import { RuleTypeParams, PartialFilter } from '../types'; import { ListArrayOrUndefined, ListArray } from '../../../../common/detection_engine/schemas/types'; -export interface RuleAlertType extends Alert { - params: RuleTypeParams; -} +export type RuleAlertType = Alert; // eslint-disable-next-line @typescript-eslint/no-explicit-any export interface IRuleStatusSOAttributes extends Record { @@ -173,11 +171,15 @@ export interface Clients { alertsClient: AlertsClient; } -export const isAlertTypes = (partialAlert: PartialAlert[]): partialAlert is RuleAlertType[] => { +export const isAlertTypes = ( + partialAlert: Array> +): partialAlert is RuleAlertType[] => { return partialAlert.every((rule) => isAlertType(rule)); }; -export const isAlertType = (partialAlert: PartialAlert): partialAlert is RuleAlertType => { +export const isAlertType = ( + partialAlert: PartialAlert +): partialAlert is RuleAlertType => { return partialAlert.alertTypeId === SIGNALS_ID; }; @@ -305,7 +307,7 @@ export interface PatchRulesOptions { version: VersionOrUndefined; exceptionsList: ListArrayOrUndefined; actions: RuleAlertAction[] | undefined; - rule: SanitizedAlert | null; + rule: SanitizedAlert | null; } export interface ReadRuleOptions { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts index c63bd01cd1813..b3e0aaae715c9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts @@ -15,13 +15,14 @@ import { addTags } from './add_tags'; import { ruleStatusSavedObjectsClientFactory } from '../signals/rule_status_saved_objects_client'; import { typeSpecificSnakeToCamel } from '../schemas/rule_converters'; import { InternalRuleUpdate } from '../schemas/rule_schemas'; +import { RuleTypeParams } from '../types'; export const updateRules = async ({ alertsClient, savedObjectsClient, defaultOutputIndex, ruleUpdate, -}: UpdateRulesOptions): Promise => { +}: UpdateRulesOptions): Promise | null> => { const existingRule = await readRules({ alertsClient, ruleId: ruleUpdate.rule_id, @@ -77,10 +78,13 @@ export const updateRules = async ({ notifyWhen: null, }; - const update = await alertsClient.update({ + /** + * TODO: Remove this use of `as` by utilizing the proper type + */ + const update = (await alertsClient.update({ id: existingRule.id, data: newInternalRule, - }); + })) as PartialAlert; if (existingRule.enabled && enabled === false) { await alertsClient.disable({ id: existingRule.id }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.test.ts index 043066faa8010..f9899fb55bb6a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.test.ts @@ -16,6 +16,7 @@ describe('create_signals', () => { size: 100, searchAfterSortId: undefined, timestampOverride: undefined, + excludeDocsWithTimestampOverride: false, }); expect(query).toEqual({ allowNoIndices: true, @@ -95,6 +96,7 @@ describe('create_signals', () => { size: 100, searchAfterSortId: '', timestampOverride: undefined, + excludeDocsWithTimestampOverride: false, }); expect(query).toEqual({ allowNoIndices: true, @@ -175,6 +177,7 @@ describe('create_signals', () => { size: 100, searchAfterSortId: fakeSortId, timestampOverride: undefined, + excludeDocsWithTimestampOverride: false, }); expect(query).toEqual({ allowNoIndices: true, @@ -256,6 +259,7 @@ describe('create_signals', () => { size: 100, searchAfterSortId: fakeSortIdNumber, timestampOverride: undefined, + excludeDocsWithTimestampOverride: false, }); expect(query).toEqual({ allowNoIndices: true, @@ -336,6 +340,7 @@ describe('create_signals', () => { size: 100, searchAfterSortId: undefined, timestampOverride: undefined, + excludeDocsWithTimestampOverride: false, }); expect(query).toEqual({ allowNoIndices: true, @@ -423,6 +428,7 @@ describe('create_signals', () => { size: 100, searchAfterSortId: undefined, timestampOverride: undefined, + excludeDocsWithTimestampOverride: false, }); expect(query).toEqual({ allowNoIndices: true, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.ts index beca56770a9ca..31a424cdbcc1b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.ts @@ -19,6 +19,7 @@ interface BuildEventsSearchQuery { sortOrder?: SortOrderOrUndefined; searchAfterSortId: string | number | undefined; timestampOverride: TimestampOverrideOrUndefined; + excludeDocsWithTimestampOverride: boolean; } export const buildEventsSearchQuery = ({ @@ -31,66 +32,65 @@ export const buildEventsSearchQuery = ({ searchAfterSortId, sortOrder, timestampOverride, + excludeDocsWithTimestampOverride, }: BuildEventsSearchQuery) => { - const timestamp = timestampOverride ?? '@timestamp'; - const docFields = - timestampOverride != null - ? [ - { - field: '@timestamp', - format: 'strict_date_optional_time', - }, - { - field: timestampOverride, - format: 'strict_date_optional_time', - }, - ] - : [ - { - field: '@timestamp', - format: 'strict_date_optional_time', - }, - ]; + const defaultTimeFields = ['@timestamp']; + const timestamps = + timestampOverride != null ? [timestampOverride, ...defaultTimeFields] : defaultTimeFields; + const docFields = timestamps.map((tstamp) => ({ + field: tstamp, + format: 'strict_date_optional_time', + })); + + const sortField = + timestampOverride != null && !excludeDocsWithTimestampOverride + ? timestampOverride + : '@timestamp'; - const filterWithTime = [ - filter, + const rangeFilter: unknown[] = [ { bool: { - filter: [ + should: [ { - bool: { - should: [ - { - range: { - [timestamp]: { - gte: from, - format: 'strict_date_optional_time', - }, - }, - }, - ], - minimum_should_match: 1, + range: { + [sortField]: { + gte: from, + format: 'strict_date_optional_time', + }, }, }, + ], + minimum_should_match: 1, + }, + }, + { + bool: { + should: [ { - bool: { - should: [ - { - range: { - [timestamp]: { - lte: to, - format: 'strict_date_optional_time', - }, - }, - }, - ], - minimum_should_match: 1, + range: { + [sortField]: { + lte: to, + format: 'strict_date_optional_time', + }, }, }, ], + minimum_should_match: 1, }, }, ]; + if (excludeDocsWithTimestampOverride) { + rangeFilter.push({ + bool: { + must_not: { + exists: { + field: timestampOverride, + }, + }, + }, + }); + } + const filterWithTime = [filter, { bool: { filter: rangeFilter } }]; const searchQuery = { allowNoIndices: true, @@ -112,7 +112,7 @@ export const buildEventsSearchQuery = ({ ...(aggregations ? { aggregations } : {}), sort: [ { - [timestamp]: { + [sortField]: { order: sortOrder ?? 'asc', }, }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/find_threshold_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/find_threshold_signals.ts index 7141b61a23e6e..239edcd1f1845 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/find_threshold_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/find_threshold_signals.ts @@ -85,5 +85,6 @@ export const findThresholdSignals = async ({ pageSize: 1, sortOrder: 'desc', buildRuleMessage, + excludeDocsWithTimestampOverride: false, }); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts index 67246a830ce90..caac728f0a136 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts @@ -183,31 +183,6 @@ describe('searchAfterAndBulkCreate', () => { }, ], }) - .mockResolvedValueOnce(sampleDocSearchResultsNoSortIdNoHits()) - .mockResolvedValueOnce(repeatedSearchResultsWithSortId(4, 1, someGuids.slice(9, 12))) - .mockResolvedValueOnce({ - took: 100, - errors: false, - items: [ - { - create: { - status: 201, - }, - }, - ], - }) - .mockResolvedValueOnce(repeatedSearchResultsWithSortId(4, 1, someGuids.slice(0, 3))) - .mockResolvedValueOnce({ - took: 100, - errors: false, - items: [ - { - create: { - status: 201, - }, - }, - ], - }) .mockResolvedValueOnce(sampleDocSearchResultsNoSortIdNoHits()); const exceptionItem = getExceptionListItemSchemaMock(); @@ -250,8 +225,8 @@ describe('searchAfterAndBulkCreate', () => { buildRuleMessage, }); expect(success).toEqual(true); - expect(mockService.callCluster).toHaveBeenCalledTimes(12); - expect(createdSignalsCount).toEqual(5); + expect(mockService.callCluster).toHaveBeenCalledTimes(8); + expect(createdSignalsCount).toEqual(3); expect(lastLookBackDate).toEqual(new Date('2020-04-20T21:27:45+0000')); }); @@ -461,7 +436,7 @@ describe('searchAfterAndBulkCreate', () => { // I don't like testing log statements since logs change but this is the best // way I can think of to ensure this section is getting hit with this test case. expect(((mockLogger.debug as unknown) as jest.Mock).mock.calls[8][0]).toContain( - 'sortIds was empty on searchResult' + 'ran out of sort ids to sort on name: "fake name" id: "fake id" rule id: "fake rule id" signals index: "fakeindex"' ); }); @@ -542,7 +517,7 @@ describe('searchAfterAndBulkCreate', () => { // I don't like testing log statements since logs change but this is the best // way I can think of to ensure this section is getting hit with this test case. expect(((mockLogger.debug as unknown) as jest.Mock).mock.calls[15][0]).toContain( - 'sortIds was empty on searchResult name: "fake name" id: "fake id" rule id: "fake rule id" signals index: "fakeindex"' + 'ran out of sort ids to sort on name: "fake name" id: "fake id" rule id: "fake rule id" signals index: "fakeindex"' ); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts index b79f758cd7503..fa47ef25a2db0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts @@ -3,6 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +/* eslint-disable complexity */ import { singleSearchAfter } from './single_search_after'; import { singleBulkCreate } from './single_bulk_create'; @@ -10,10 +11,12 @@ import { filterEventsAgainstList } from './filters/filter_events_against_list'; import { sendAlertTelemetryEvents } from './send_telemetry_events'; import { createSearchAfterReturnType, + createSearchResultReturnType, createSearchAfterReturnTypeFromResponse, createTotalHitsFromSearchResult, getSignalTimeTuples, mergeReturns, + mergeSearchResults, } from './utils'; import { SearchAfterAndBulkCreateParams, SearchAfterAndBulkCreateReturnType } from './types'; @@ -49,6 +52,9 @@ export const searchAfterAndBulkCreate = async ({ // sortId tells us where to start our next consecutive search_after query let sortId: string | undefined; + let hasSortId = true; // default to true so we execute the search on initial run + let backupSortId: string | undefined; + let hasBackupSortId = ruleParams.timestampOverride ? true : false; // signalsCreatedCount keeps track of how many signals we have created, // to ensure we don't exceed maxSignals @@ -78,10 +84,11 @@ export const searchAfterAndBulkCreate = async ({ signalsCreatedCount = 0; while (signalsCreatedCount < tuple.maxSignals) { try { + let mergedSearchResults = createSearchResultReturnType(); logger.debug(buildRuleMessage(`sortIds: ${sortId}`)); // perform search_after with optionally undefined sortId - const { searchResult, searchDuration, searchErrors } = await singleSearchAfter({ + const singleSearchAfterPromise = singleSearchAfter({ buildRuleMessage, searchAfterSortId: sortId, index: inputIndexPattern, @@ -92,23 +99,92 @@ export const searchAfterAndBulkCreate = async ({ filter, pageSize: tuple.maxSignals < pageSize ? Math.ceil(tuple.maxSignals) : pageSize, // maximum number of docs to receive per search result. timestampOverride: ruleParams.timestampOverride, + excludeDocsWithTimestampOverride: false, }); - toReturn = mergeReturns([ - toReturn, - createSearchAfterReturnTypeFromResponse({ - searchResult, + + // if there is a timestampOverride param we always want to do a secondary search against @timestamp + if (ruleParams.timestampOverride != null && hasBackupSortId) { + // only execute search if we have something to sort on or if it is the first search + const singleSearchAfterDefaultTimestamp = singleSearchAfter({ + buildRuleMessage, + searchAfterSortId: backupSortId, + index: inputIndexPattern, + from: tuple.from.toISOString(), + to: tuple.to.toISOString(), + services, + logger, + filter, + pageSize: tuple.maxSignals < pageSize ? Math.ceil(tuple.maxSignals) : pageSize, // maximum number of docs to receive per search result. timestampOverride: ruleParams.timestampOverride, - }), - createSearchAfterReturnType({ - searchAfterTimes: [searchDuration], - errors: searchErrors, - }), - ]); + excludeDocsWithTimestampOverride: true, + }); + const { + searchResult: searchResultB, + searchDuration: searchDurationB, + searchErrors: searchErrorsB, + } = await singleSearchAfterDefaultTimestamp; + + // call this function setSortIdOrExit() + const lastSortId = searchResultB?.hits?.hits[searchResultB.hits.hits.length - 1]?.sort; + if (lastSortId != null && lastSortId.length !== 0) { + backupSortId = lastSortId[0]; + hasBackupSortId = true; + } else { + // if no sort id on backup search and the initial search result was also empty + logger.debug(buildRuleMessage('backupSortIds was empty on searchResultB')); + hasBackupSortId = false; + } + + mergedSearchResults = mergeSearchResults([mergedSearchResults, searchResultB]); + + // merge the search result from the secondary search with the first + toReturn = mergeReturns([ + toReturn, + createSearchAfterReturnTypeFromResponse({ + searchResult: mergedSearchResults, + timestampOverride: undefined, + }), + createSearchAfterReturnType({ + searchAfterTimes: [searchDurationB], + errors: searchErrorsB, + }), + ]); + } + + if (hasSortId) { + // only execute search if we have something to sort on or if it is the first search + const { searchResult, searchDuration, searchErrors } = await singleSearchAfterPromise; + mergedSearchResults = mergeSearchResults([mergedSearchResults, searchResult]); + toReturn = mergeReturns([ + toReturn, + createSearchAfterReturnTypeFromResponse({ + searchResult: mergedSearchResults, + timestampOverride: ruleParams.timestampOverride, + }), + createSearchAfterReturnType({ + searchAfterTimes: [searchDuration], + errors: searchErrors, + }), + ]); + + // we are guaranteed to have searchResult hits at this point + // because we check before if the totalHits or + // searchResult.hits.hits.length is 0 + // call this function setSortIdOrExit() + const lastSortId = searchResult.hits.hits[searchResult.hits.hits.length - 1]?.sort; + if (lastSortId != null && lastSortId.length !== 0) { + sortId = lastSortId[0]; + hasSortId = true; + } else { + hasSortId = false; + } + } + // determine if there are any candidate signals to be processed - const totalHits = createTotalHitsFromSearchResult({ searchResult }); + const totalHits = createTotalHitsFromSearchResult({ searchResult: mergedSearchResults }); logger.debug(buildRuleMessage(`totalHits: ${totalHits}`)); logger.debug( - buildRuleMessage(`searchResult.hit.hits.length: ${searchResult.hits.hits.length}`) + buildRuleMessage(`searchResult.hit.hits.length: ${mergedSearchResults.hits.hits.length}`) ); // search results yielded zero hits so exit @@ -119,7 +195,7 @@ export const searchAfterAndBulkCreate = async ({ // e.g. totalHits was 156, index 50 of 100 results, do another search-after // this time with a new sortId, index 22 of the remaining 56, get another sortId // search with that sortId, total is still 156 but the hits.hits array is empty. - if (totalHits === 0 || searchResult.hits.hits.length === 0) { + if (totalHits === 0 || mergedSearchResults.hits.hits.length === 0) { logger.debug( buildRuleMessage( `${ @@ -137,7 +213,7 @@ export const searchAfterAndBulkCreate = async ({ listClient, exceptionsList, logger, - eventSearchResult: searchResult, + eventSearchResult: mergedSearchResults, buildRuleMessage, }); @@ -205,14 +281,8 @@ export const searchAfterAndBulkCreate = async ({ ); } - // we are guaranteed to have searchResult hits at this point - // because we check before if the totalHits or - // searchResult.hits.hits.length is 0 - const lastSortId = searchResult.hits.hits[searchResult.hits.hits.length - 1].sort; - if (lastSortId != null && lastSortId.length !== 0) { - sortId = lastSortId[0]; - } else { - logger.debug(buildRuleMessage('sortIds was empty on searchResult')); + if (!hasSortId && !hasBackupSortId) { + logger.debug(buildRuleMessage('ran out of sort ids to sort on')); break; } } catch (exc: unknown) { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_params_schema.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_params_schema.ts index 50e740e81830f..aede91c5af143 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_params_schema.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_params_schema.ts @@ -8,7 +8,7 @@ import { schema, TypeOf } from '@kbn/config-schema'; import { DEFAULT_MAX_SIGNALS } from '../../../../common/constants'; -const signalSchema = schema.object({ +export const signalSchema = schema.object({ anomalyThreshold: schema.maybe(schema.number()), author: schema.arrayOf(schema.string(), { defaultValue: [] }), buildingBlockType: schema.nullable(schema.string()), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts index 9a40573095a1a..f8983061d7a7a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts @@ -16,6 +16,7 @@ import { getListsClient, getExceptions, sortExceptionItems, + checkPrivileges, } from './utils'; import { parseScheduleDates } from '../../../../common/detection_engine/parse_schedule_dates'; import { RuleExecutorOptions, SearchAfterAndBulkCreateReturnType } from './types'; @@ -42,6 +43,7 @@ jest.mock('./utils', () => { getListsClient: jest.fn(), getExceptions: jest.fn(), sortExceptionItems: jest.fn(), + checkPrivileges: jest.fn(), }; }); jest.mock('../notifications/schedule_notification_actions'); @@ -49,7 +51,10 @@ jest.mock('./find_ml_signals'); jest.mock('./bulk_create_ml_signals'); jest.mock('../../../../common/detection_engine/parse_schedule_dates'); -const getPayload = (ruleAlert: RuleAlertType, services: AlertServicesMock) => ({ +const getPayload = ( + ruleAlert: RuleAlertType, + services: AlertServicesMock +): RuleExecutorOptions => ({ alertId: ruleAlert.id, services, params: { @@ -102,6 +107,7 @@ describe('rules_notification_alert_type', () => { find: jest.fn(), goingToRun: jest.fn(), error: jest.fn(), + partialFailure: jest.fn(), }; (ruleStatusServiceFactory as jest.Mock).mockReturnValue(ruleStatusService); (getGapBetweenRuns as jest.Mock).mockReturnValue(moment.duration(0)); @@ -121,6 +127,21 @@ describe('rules_notification_alert_type', () => { searchAfterTimes: [], createdSignalsCount: 10, }); + (checkPrivileges as jest.Mock).mockImplementation((_, indices) => { + return { + index: indices.reduce( + (acc: { index: { [x: string]: { read: boolean } } }, index: string) => { + return { + [index]: { + read: true, + }, + ...acc, + }; + }, + {} + ), + }; + }); alertServices.callCluster.mockResolvedValue({ hits: { total: { value: 10 }, @@ -167,6 +188,55 @@ describe('rules_notification_alert_type', () => { }); }); + it('should set a partial failure for when rules cannot read ALL provided indices', async () => { + (checkPrivileges as jest.Mock).mockResolvedValueOnce({ + username: 'elastic', + has_all_requested: false, + cluster: {}, + index: { + 'myfa*': { + read: true, + }, + 'anotherindex*': { + read: true, + }, + 'some*': { + read: false, + }, + }, + application: {}, + }); + payload.params.index = ['some*', 'myfa*', 'anotherindex*']; + await alert.executor(payload); + expect(ruleStatusService.partialFailure).toHaveBeenCalled(); + expect(ruleStatusService.partialFailure.mock.calls[0][0]).toContain( + 'Missing required read permissions on indexes: ["some*"]' + ); + }); + + it('should set a failure status for when rules cannot read ANY provided indices', async () => { + (checkPrivileges as jest.Mock).mockResolvedValueOnce({ + username: 'elastic', + has_all_requested: false, + cluster: {}, + index: { + 'myfa*': { + read: false, + }, + 'some*': { + read: false, + }, + }, + application: {}, + }); + payload.params.index = ['some*', 'myfa*']; + await alert.executor(payload); + expect(ruleStatusService.error).toHaveBeenCalled(); + expect(ruleStatusService.error.mock.calls[0][0]).toContain( + 'The rule does not have read privileges to any of the following indices: ["myfa*","some*"]' + ); + }); + it('should NOT warn about the gap between runs if gap small', async () => { (getGapBetweenRuns as jest.Mock).mockReturnValue(moment.duration(1, 'm')); (getGapMaxCatchupRatio as jest.Mock).mockReturnValue({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts index 3928228357d4c..8a219d926a96d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts @@ -7,6 +7,7 @@ /* eslint-disable complexity */ import { Logger, KibanaRequest } from 'src/core/server'; +import { partition } from 'lodash'; import { SIGNALS_ID, @@ -41,6 +42,7 @@ import { createSearchAfterReturnType, mergeReturns, createSearchAfterReturnTypeFromResponse, + checkPrivileges, } from './utils'; import { signalParamsSchema } from './signal_params_schema'; import { siemRuleActionGroups } from './siem_rule_action_groups'; @@ -66,6 +68,7 @@ import { getIndexVersion } from '../routes/index/get_index_version'; import { MIN_EQL_RULE_INDEX_VERSION } from '../routes/index/get_signals_template'; import { filterEventsAgainstList } from './filters/filter_events_against_list'; import { isOutdated } from '../migrations/helpers'; +import { RuleTypeParams } from '../types'; export const signalRulesAlertType = ({ logger, @@ -86,7 +89,16 @@ export const signalRulesAlertType = ({ actionGroups: siemRuleActionGroups, defaultActionGroupId: 'default', validate: { - params: signalParamsSchema(), + /** + * TODO: Fix typing inconsistancy between `RuleTypeParams` and `CreateRulesOptions` + * Once that's done, you should be able to do: + * ``` + * params: signalParamsSchema(), + * ``` + */ + params: (signalParamsSchema() as unknown) as { + validate: (object: unknown) => RuleTypeParams; + }, }, producer: SERVER_APP_ID, minimumLicenseRequired: 'basic', @@ -161,8 +173,48 @@ export const signalRulesAlertType = ({ logger.debug(buildRuleMessage('[+] Starting Signal Rule execution')); logger.debug(buildRuleMessage(`interval: ${interval}`)); + let wroteStatus = false; await ruleStatusService.goingToRun(); + // check if rule has permissions to access given index pattern + // move this collection of lines into a function in utils + // so that we can use it in create rules route, bulk, etc. + try { + const inputIndex = await getInputIndex(services, version, index); + const privileges = await checkPrivileges(services, inputIndex); + + const indexNames = Object.keys(privileges.index); + const [indexesWithReadPrivileges, indexesWithNoReadPrivileges] = partition( + indexNames, + (indexName) => privileges.index[indexName].read + ); + + if (indexesWithReadPrivileges.length > 0 && indexesWithNoReadPrivileges.length > 0) { + // some indices have read privileges others do not. + // set a partial failure status + const errorString = `Missing required read permissions on indexes: ${JSON.stringify( + indexesWithNoReadPrivileges + )}`; + logger.debug(buildRuleMessage(errorString)); + await ruleStatusService.partialFailure(errorString); + wroteStatus = true; + } else if ( + indexesWithReadPrivileges.length === 0 && + indexesWithNoReadPrivileges.length === indexNames.length + ) { + // none of the indices had read privileges so set the status to failed + // since we can't search on any indices we do not have read privileges on + const errorString = `The rule does not have read privileges to any of the following indices: ${JSON.stringify( + indexesWithNoReadPrivileges + )}`; + logger.debug(buildRuleMessage(errorString)); + await ruleStatusService.error(errorString); + wroteStatus = true; + } + } catch (exc) { + logger.error(buildRuleMessage(`Check privileges failed to execute ${exc}`)); + } + const gap = getGapBetweenRuns({ previousStartedAt, interval, from, to }); if (gap != null && gap.asMilliseconds() > 0) { const fromUnit = from[from.length - 1]; @@ -590,7 +642,7 @@ export const signalRulesAlertType = ({ `[+] Finished indexing ${result.createdSignalsCount} signals into ${outputIndex}` ) ); - if (!hasError) { + if (!hasError && !wroteStatus) { await ruleStatusService.success('succeeded', { bulkCreateTimeDurations: result.bulkCreateTimes, searchAfterTimeDurations: result.searchAfterTimes, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.test.ts index c4869f024a977..12d91dcde2ff7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.test.ts @@ -40,6 +40,7 @@ describe('singleSearchAfter', () => { filter: undefined, timestampOverride: undefined, buildRuleMessage, + excludeDocsWithTimestampOverride: false, }); expect(searchResult).toEqual(sampleDocSearchResultsNoSortId()); }); @@ -56,6 +57,7 @@ describe('singleSearchAfter', () => { filter: undefined, timestampOverride: undefined, buildRuleMessage, + excludeDocsWithTimestampOverride: false, }); expect(searchErrors).toEqual([]); }); @@ -104,9 +106,10 @@ describe('singleSearchAfter', () => { filter: undefined, timestampOverride: undefined, buildRuleMessage, + excludeDocsWithTimestampOverride: false, }); expect(searchErrors).toEqual([ - 'reason: "some reason" type: "some type" caused by reason: "some reason" caused by type: "some type"', + 'index: "index-123" reason: "some reason" type: "some type" caused by reason: "some reason" caused by type: "some type"', ]); }); test('if singleSearchAfter works with a given sort id', async () => { @@ -123,6 +126,7 @@ describe('singleSearchAfter', () => { filter: undefined, timestampOverride: undefined, buildRuleMessage, + excludeDocsWithTimestampOverride: false, }); expect(searchResult).toEqual(sampleDocSearchResultsWithSortId()); }); @@ -143,6 +147,7 @@ describe('singleSearchAfter', () => { filter: undefined, timestampOverride: undefined, buildRuleMessage, + excludeDocsWithTimestampOverride: false, }) ).rejects.toThrow('Fake Error'); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.ts index 23ef9fcea8e53..79e1f9896d63f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.ts @@ -29,6 +29,7 @@ interface SingleSearchAfterParams { filter: unknown; timestampOverride: TimestampOverrideOrUndefined; buildRuleMessage: BuildRuleMessage; + excludeDocsWithTimestampOverride: boolean; } // utilize search_after for paging results into bulk. @@ -45,6 +46,7 @@ export const singleSearchAfter = async ({ sortOrder, timestampOverride, buildRuleMessage, + excludeDocsWithTimestampOverride, }: SingleSearchAfterParams): Promise<{ searchResult: SignalSearchResponse; searchDuration: string; @@ -61,6 +63,7 @@ export const singleSearchAfter = async ({ sortOrder, searchAfterSortId, timestampOverride, + excludeDocsWithTimestampOverride, }); const start = performance.now(); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold_find_previous_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold_find_previous_signals.ts index 960693bc703d6..6e7f63deb06f7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold_find_previous_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold_find_previous_signals.ts @@ -83,5 +83,6 @@ export const findPreviousThresholdSignals = async ({ filter, pageSize: 0, buildRuleMessage, + excludeDocsWithTimestampOverride: false, }); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts index 4167d056df885..62339f50d939c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts @@ -11,6 +11,8 @@ import { RulesSchema } from '../../../../common/detection_engine/schemas/respons import { AlertType, AlertTypeState, + AlertInstanceState, + AlertInstanceContext, AlertExecutorOptions, AlertServices, } from '../../../../../alerts/server'; @@ -128,19 +130,27 @@ export type BaseSignalHit = BaseHit; export type EqlSignalSearchResponse = EqlSearchResponse; -export type RuleExecutorOptions = Omit & { - params: RuleTypeParams; -}; +export type RuleExecutorOptions = AlertExecutorOptions< + RuleTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext +>; // This returns true because by default a RuleAlertTypeDefinition is an AlertType // since we are only increasing the strictness of params. -export const isAlertExecutor = (obj: SignalRuleAlertTypeDefinition): obj is AlertType => { +export const isAlertExecutor = ( + obj: SignalRuleAlertTypeDefinition +): obj is AlertType => { return true; }; -export type SignalRuleAlertTypeDefinition = Omit & { - executor: ({ services, params, state }: RuleExecutorOptions) => Promise; -}; +export type SignalRuleAlertTypeDefinition = AlertType< + RuleTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext +>; export interface Ancestor { rule?: string; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts index 073e30bbc6e26..b410fb7c35be0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts @@ -879,7 +879,7 @@ describe('utils', () => { ]; const createdErrors = createErrorsFromShard({ errors }); expect(createdErrors).toEqual([ - 'reason: "some reason" type: "some type" caused by reason: "some reason" caused by type: "some type"', + 'index: "index-123" reason: "some reason" type: "some type" caused by reason: "some reason" caused by type: "some type"', ]); }); @@ -918,8 +918,8 @@ describe('utils', () => { ]; const createdErrors = createErrorsFromShard({ errors }); expect(createdErrors).toEqual([ - 'reason: "some reason" type: "some type" caused by reason: "some reason" caused by type: "some type"', - 'reason: "some reason 2" type: "some type 2" caused by reason: "some reason 2" caused by type: "some type 2"', + 'index: "index-123" reason: "some reason" type: "some type" caused by reason: "some reason" caused by type: "some type"', + 'index: "index-345" reason: "some reason 2" type: "some type 2" caused by reason: "some reason 2" caused by type: "some type 2"', ]); }); @@ -933,7 +933,7 @@ describe('utils', () => { }, ]; const createdErrors = createErrorsFromShard({ errors }); - expect(createdErrors).toEqual(['']); + expect(createdErrors).toEqual(['index: "index-123"']); }); test('You can have a single value for the shard errors and get expected output without extra spaces anywhere', () => { @@ -948,7 +948,9 @@ describe('utils', () => { }, ]; const createdErrors = createErrorsFromShard({ errors }); - expect(createdErrors).toEqual(['reason: "some reason something went wrong"']); + expect(createdErrors).toEqual([ + 'index: "index-123" reason: "some reason something went wrong"', + ]); }); test('You can have two values for the shard errors and get expected output with one space exactly between the two values', () => { @@ -965,7 +967,7 @@ describe('utils', () => { ]; const createdErrors = createErrorsFromShard({ errors }); expect(createdErrors).toEqual([ - 'reason: "some reason something went wrong" caused by type: "some type"', + 'index: "index-123" reason: "some reason something went wrong" caused by type: "some type"', ]); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts index 18f6e8d127b1b..ab14643f30e41 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts @@ -52,6 +52,20 @@ export const shorthandMap = { }, }; +export const checkPrivileges = async (services: AlertServices, indices: string[]) => + services.callCluster('transport.request', { + path: '/_security/user/_has_privileges', + method: 'POST', + body: { + index: [ + { + names: indices ?? [], + privileges: ['read'], + }, + ], + }, + }); + export const getGapMaxCatchupRatio = ({ logger, previousStartedAt, @@ -516,6 +530,7 @@ export const getSignalTimeTuples = ({ export const createErrorsFromShard = ({ errors }: { errors: ShardError[] }): string[] => { return errors.map((error) => { const { + index, reason: { reason, type, @@ -527,6 +542,7 @@ export const createErrorsFromShard = ({ errors }: { errors: ShardError[] }): str } = error; return [ + ...(index != null ? [`index: "${index}"`] : []), ...(reason != null ? [`reason: "${reason}"`] : []), ...(type != null ? [`type: "${type}"`] : []), ...(causedByReason != null ? [`caused by reason: "${causedByReason}"`] : []), @@ -615,6 +631,25 @@ export const createSearchAfterReturnType = ({ }; }; +export const createSearchResultReturnType = (): SignalSearchResponse => { + return { + took: 0, + timed_out: false, + _shards: { + total: 0, + successful: 0, + failed: 0, + skipped: 0, + failures: [], + }, + hits: { + total: 0, + max_score: 0, + hits: [], + }, + }; +}; + export const mergeReturns = ( searchAfters: SearchAfterAndBulkCreateReturnType[] ): SearchAfterAndBulkCreateReturnType => { @@ -651,6 +686,52 @@ export const mergeReturns = ( }); }; +export const mergeSearchResults = (searchResults: SignalSearchResponse[]) => { + return searchResults.reduce((prev, next) => { + const { + took: existingTook, + timed_out: existingTimedOut, + // _scroll_id: existingScrollId, + _shards: existingShards, + // aggregations: existingAggregations, + hits: existingHits, + } = prev; + + const { + took: newTook, + timed_out: newTimedOut, + _scroll_id: newScrollId, + _shards: newShards, + aggregations: newAggregations, + hits: newHits, + } = next; + + return { + took: Math.max(newTook, existingTook), + timed_out: newTimedOut && existingTimedOut, + _scroll_id: newScrollId, + _shards: { + total: newShards.total + existingShards.total, + successful: newShards.successful + existingShards.successful, + failed: newShards.failed + existingShards.failed, + skipped: newShards.skipped + existingShards.skipped, + failures: [ + ...(existingShards.failures != null ? existingShards.failures : []), + ...(newShards.failures != null ? newShards.failures : []), + ], + }, + aggregations: newAggregations, + hits: { + total: + createTotalHitsFromSearchResult({ searchResult: prev }) + + createTotalHitsFromSearchResult({ searchResult: next }), + max_score: Math.max(newHits.max_score, existingHits.max_score), + hits: [...existingHits.hits, ...newHits.hits], + }, + }; + }); +}; + export const createTotalHitsFromSearchResult = ({ searchResult, }: { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/types.ts index 57535178c5280..90f2ce638855b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/types.ts @@ -51,10 +51,11 @@ import { import { LegacyCallAPIOptions } from '../../../../../../src/core/server'; import { Filter } from '../../../../../../src/plugins/data/server'; import { ListArrayOrUndefined } from '../../../common/detection_engine/schemas/types'; +import { AlertTypeParams } from '../../../../alerts/common'; export type PartialFilter = Partial; -export interface RuleTypeParams { +export interface RuleTypeParams extends AlertTypeParams { anomalyThreshold: AnomalyThresholdOrUndefined; author: AuthorOrUndefined; buildingBlockType: BuildingBlockTypeOrUndefined; diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts b/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts index 65c69966f7caa..822a60da09167 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts @@ -104,7 +104,7 @@ export class TelemetryEventsSender { public async fetchDiagnosticAlerts(executeFrom: string, executeTo: string) { const query = { expand_wildcards: 'open,hidden', - index: 'logs-endpoint.diagnostic.collection-*', + index: '.logs-endpoint.diagnostic.collection-*', ignore_unavailable: true, size: this.maxQueueSize, body: { diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/task.ts b/x-pack/plugins/security_solution/server/lib/telemetry/task.ts index 28b8524f64516..a723cb9a3e637 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/task.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/task.ts @@ -36,6 +36,8 @@ export class TelemetryDiagTask { title: 'Security Solution Telemetry Diagnostics task', timeout: TelemetryDiagTaskConstants.TIMEOUT, createTaskRunner: ({ taskInstance }: { taskInstance: ConcreteTaskInstance }) => { + const { state } = taskInstance; + return { run: async () => { const executeTo = moment().utc().toISOString(); @@ -43,11 +45,13 @@ export class TelemetryDiagTask { executeTo, taskInstance.state?.lastExecutionTimestamp ); - await this.runTask(taskInstance.id, executeFrom, executeTo); + const hits = await this.runTask(taskInstance.id, executeFrom, executeTo); return { state: { lastExecutionTimestamp: executeTo, + lastDiagAlertCount: hits, + runs: (state.runs || 0) + 1, }, }; }, @@ -81,7 +85,7 @@ export class TelemetryDiagTask { schedule: { interval: TelemetryDiagTaskConstants.INTERVAL, }, - state: {}, + state: { runs: 0 }, params: { version: TelemetryDiagTaskConstants.VERSION }, }); } catch (e) { @@ -97,13 +101,13 @@ export class TelemetryDiagTask { this.logger.debug(`Running task ${taskId}`); if (taskId !== this.getTaskId()) { this.logger.debug(`Outdated task running: ${taskId}`); - return; + return 0; } const isOptedIn = await this.sender.isTelemetryOptedIn(); if (!isOptedIn) { this.logger.debug(`Telemetry is not opted-in.`); - return; + return 0; } const response = await this.sender.fetchDiagnosticAlerts(searchFrom, searchTo); @@ -111,11 +115,12 @@ export class TelemetryDiagTask { const hits = response.hits?.hits || []; if (!Array.isArray(hits) || !hits.length) { this.logger.debug('no diagnostic alerts retrieved'); - return; + return 0; } + this.logger.debug(`Received ${hits.length} diagnostic alerts`); const diagAlerts: TelemetryEvent[] = hits.map((h) => h._source); - this.logger.debug(`Received ${diagAlerts.length} diagnostic alerts`); this.sender.queueTelemetryEvents(diagAlerts); + return diagAlerts.length; }; } diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/overview/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/overview/__mocks__/index.ts index b1f9b04daa1c1..574be4b45fe26 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/overview/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/overview/__mocks__/index.ts @@ -322,7 +322,7 @@ export const formattedSearchStrategyResponse = { endgameSecurity: 0, filebeatSystemModule: 1793, winlogbeatSecurity: 42, - winlogbeatMWSysmonOperational: null, + winlogbeatMWSysmonOperational: 1781, }, }; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/overview/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/overview/index.ts index 61c228a5fd164..4a9d4c7af103c 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/overview/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/overview/index.ts @@ -58,7 +58,7 @@ export const hostOverview: SecuritySolutionFactory = { winlogbeatMWSysmonOperational: getOr( null, 'winlog_module.mwsysmon_operational_event_count.doc_count', - response + aggregations ), }, }; diff --git a/x-pack/plugins/security_solution/server/usage/collector.ts b/x-pack/plugins/security_solution/server/usage/collector.ts index f4c5462267920..0d44ab42d8044 100644 --- a/x-pack/plugins/security_solution/server/usage/collector.ts +++ b/x-pack/plugins/security_solution/server/usage/collector.ts @@ -78,12 +78,12 @@ export const registerCollector: RegisterCollector = ({ }, }, isReady: () => kibanaIndex.length > 0, - fetch: async ({ callCluster }: CollectorFetchContext): Promise => { + fetch: async ({ esClient }: CollectorFetchContext): Promise => { const savedObjectsClient = await getInternalSavedObjectsClient(core); const [detections, endpoints] = await Promise.allSettled([ fetchDetectionsUsage( kibanaIndex, - callCluster, + esClient, ml, (savedObjectsClient as unknown) as SavedObjectsClientContract ), diff --git a/x-pack/plugins/security_solution/server/usage/detections/detections.test.ts b/x-pack/plugins/security_solution/server/usage/detections/detections.test.ts index 0d2d610c53cdc..f60f863414b2f 100644 --- a/x-pack/plugins/security_solution/server/usage/detections/detections.test.ts +++ b/x-pack/plugins/security_solution/server/usage/detections/detections.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LegacyAPICaller, SavedObjectsClientContract } from '../../../../../../src/core/server'; +import { ElasticsearchClient, SavedObjectsClientContract } from '../../../../../../src/core/server'; import { elasticsearchServiceMock } from '../../../../../../src/core/server/mocks'; import { mlServicesMock } from '../../lib/machine_learning/mocks'; import { @@ -16,22 +16,17 @@ import { fetchDetectionsUsage } from './index'; describe('Detections Usage', () => { describe('fetchDetectionsUsage()', () => { - let callClusterMock: jest.Mocked; + let esClientMock: jest.Mocked; let savedObjectsClientMock: jest.Mocked; let mlMock: ReturnType; beforeEach(() => { - callClusterMock = elasticsearchServiceMock.createLegacyClusterClient().callAsInternalUser; + esClientMock = elasticsearchServiceMock.createClusterClient().asInternalUser; mlMock = mlServicesMock.create(); }); it('returns zeroed counts if both calls are empty', async () => { - const result = await fetchDetectionsUsage( - '', - callClusterMock, - mlMock, - savedObjectsClientMock - ); + const result = await fetchDetectionsUsage('', esClientMock, mlMock, savedObjectsClientMock); expect(result).toEqual({ detection_rules: { @@ -58,13 +53,9 @@ describe('Detections Usage', () => { }); it('tallies rules data given rules results', async () => { - (callClusterMock as jest.Mock).mockResolvedValue(getMockRulesResponse()); - const result = await fetchDetectionsUsage( - '', - callClusterMock, - mlMock, - savedObjectsClientMock - ); + (esClientMock.search as jest.Mock).mockResolvedValue({ body: getMockRulesResponse() }); + + const result = await fetchDetectionsUsage('', esClientMock, mlMock, savedObjectsClientMock); expect(result).toEqual( expect.objectContaining({ @@ -92,12 +83,7 @@ describe('Detections Usage', () => { jobsSummary: mockJobSummary, }); - const result = await fetchDetectionsUsage( - '', - callClusterMock, - mlMock, - savedObjectsClientMock - ); + const result = await fetchDetectionsUsage('', esClientMock, mlMock, savedObjectsClientMock); expect(result).toEqual( expect.objectContaining({ diff --git a/x-pack/plugins/security_solution/server/usage/detections/detections_helpers.ts b/x-pack/plugins/security_solution/server/usage/detections/detections_helpers.ts index 1a0e821ba47bc..804ea878108f3 100644 --- a/x-pack/plugins/security_solution/server/usage/detections/detections_helpers.ts +++ b/x-pack/plugins/security_solution/server/usage/detections/detections_helpers.ts @@ -4,12 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SearchParams } from 'elasticsearch'; - import { - LegacyAPICaller, + ElasticsearchClient, SavedObjectsClientContract, KibanaRequest, + SearchResponse, } from '../../../../../../src/core/server'; import { MlPluginSetup } from '../../../../ml/server'; import { SIGNALS_ID, INTERNAL_IMMUTABLE_KEY } from '../../../common/constants'; @@ -22,6 +21,26 @@ interface DetectionsMetric { isEnabled: boolean; } +interface RuleSearchBody { + query: { + bool: { + filter: { + term: { [key: string]: string }; + }; + }; + }; +} +interface RuleSearchParams { + body: RuleSearchBody; + filterPath: string[]; + ignoreUnavailable: boolean; + index: string; + size: number; +} +interface RuleSearchResult { + alert: { enabled: boolean; tags: string[] }; +} + const isElasticRule = (tags: string[]) => tags.includes(`${INTERNAL_IMMUTABLE_KEY}:true`); /** @@ -135,10 +154,10 @@ const updateMlJobsUsage = (jobMetric: DetectionsMetric, usage: MlJobsUsage): MlJ export const getRulesUsage = async ( index: string, - callCluster: LegacyAPICaller + esClient: ElasticsearchClient ): Promise => { let rulesUsage: DetectionRulesUsage = initialRulesUsage; - const ruleSearchOptions: SearchParams = { + const ruleSearchOptions: RuleSearchParams = { body: { query: { bool: { filter: { term: { 'alert.alertTypeId': SIGNALS_ID } } } } }, filterPath: ['hits.hits._source.alert.enabled', 'hits.hits._source.alert.tags'], ignoreUnavailable: true, @@ -147,8 +166,7 @@ export const getRulesUsage = async ( }; try { - const ruleResults = await callCluster<{ alert: { enabled: boolean; tags: string[] } }>( - 'search', + const { body: ruleResults } = await esClient.search>( ruleSearchOptions ); diff --git a/x-pack/plugins/security_solution/server/usage/detections/index.ts b/x-pack/plugins/security_solution/server/usage/detections/index.ts index 1f43d3186f2fd..63e28f3c66009 100644 --- a/x-pack/plugins/security_solution/server/usage/detections/index.ts +++ b/x-pack/plugins/security_solution/server/usage/detections/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LegacyAPICaller, SavedObjectsClientContract } from '../../../../../../src/core/server'; +import { ElasticsearchClient, SavedObjectsClientContract } from '../../../../../../src/core/server'; import { getMlJobsUsage, getRulesUsage, @@ -40,12 +40,12 @@ export const defaultDetectionsUsage = { export const fetchDetectionsUsage = async ( kibanaIndex: string, - callCluster: LegacyAPICaller, + esClient: ElasticsearchClient, ml: MlPluginSetup | undefined, savedObjectClient: SavedObjectsClientContract ): Promise => { const [rulesUsage, mlJobsUsage] = await Promise.allSettled([ - getRulesUsage(kibanaIndex, callCluster), + getRulesUsage(kibanaIndex, esClient), getMlJobsUsage(ml, savedObjectClient), ]); diff --git a/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.test.ts b/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.test.ts index ea8770b7843cf..747e37e7db32b 100644 --- a/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.test.ts +++ b/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.test.ts @@ -12,7 +12,10 @@ import { ILicense, LicensingPluginSetup } from '../../../licensing/server'; import { UsageStats } from '../usage_stats'; import { usageStatsClientMock } from '../usage_stats/usage_stats_client.mock'; import { usageStatsServiceMock } from '../usage_stats/usage_stats_service.mock'; -import { pluginInitializerContextConfigMock } from 'src/core/server/mocks'; +import { + elasticsearchServiceMock, + pluginInitializerContextConfigMock, +} from 'src/core/server/mocks'; import { createCollectorFetchContextMock } from 'src/plugins/usage_collection/server/mocks'; interface SetupOpts { @@ -74,31 +77,39 @@ function setup({ }; } -const defaultCallClusterMock = jest.fn().mockResolvedValue({ - hits: { - total: { - value: 2, +const defaultEsClientSearchMock = jest.fn().mockResolvedValue({ + body: { + hits: { + total: { + value: 2, + }, }, - }, - aggregations: { - disabledFeatures: { - buckets: [ - { - key: 'feature1', - doc_count: 1, - }, - ], + aggregations: { + disabledFeatures: { + buckets: [ + { + key: 'feature1', + doc_count: 1, + }, + ], + }, }, }, }); -const getMockFetchContext = (mockedCallCluster: jest.Mock) => { +const getMockFetchContext = (mockedEsClient: any) => { return { ...createCollectorFetchContextMock(), - callCluster: mockedCallCluster, + esClient: mockedEsClient, }; }; +const getMockedEsClient = (esClientMock: jest.Mock) => { + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + esClient.search = esClientMock; + return esClient; +}; + describe('error handling', () => { it('handles a 404 when searching for space usage', async () => { const { features, licensing, usageCollection, usageStatsService } = setup({ @@ -110,8 +121,10 @@ describe('error handling', () => { licensing, usageStatsServicePromise: Promise.resolve(usageStatsService), }); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + esClient.search.mockRejectedValue({ status: 404 }); - await collector.fetch(getMockFetchContext(jest.fn().mockRejectedValue({ status: 404 }))); + await collector.fetch(getMockFetchContext(esClient)); }); it('throws error for a non-404', async () => { @@ -124,13 +137,13 @@ describe('error handling', () => { licensing, usageStatsServicePromise: Promise.resolve(usageStatsService), }); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; const statusCodes = [401, 402, 403, 500]; for (const statusCode of statusCodes) { const error = { status: statusCode }; - await expect( - collector.fetch(getMockFetchContext(jest.fn().mockRejectedValue(error))) - ).rejects.toBe(error); + esClient.search.mockRejectedValue(error); + await expect(collector.fetch(getMockFetchContext(esClient))).rejects.toBe(error); } }); }); @@ -148,9 +161,10 @@ describe('with a basic license', () => { licensing, usageStatsServicePromise: Promise.resolve(usageStatsService), }); - usageData = await collector.fetch(getMockFetchContext(defaultCallClusterMock)); + const esClient = getMockedEsClient(defaultEsClientSearchMock); + usageData = await collector.fetch(getMockFetchContext(esClient)); - expect(defaultCallClusterMock).toHaveBeenCalledWith('search', { + expect(defaultEsClientSearchMock).toHaveBeenCalledWith({ body: { aggs: { disabledFeatures: { @@ -206,7 +220,9 @@ describe('with no license', () => { licensing, usageStatsServicePromise: Promise.resolve(usageStatsService), }); - usageData = await collector.fetch(getMockFetchContext(defaultCallClusterMock)); + const esClient = getMockedEsClient(defaultEsClientSearchMock); + + usageData = await collector.fetch(getMockFetchContext(esClient)); }); test('sets enabled to false', () => { @@ -245,7 +261,9 @@ describe('with platinum license', () => { licensing, usageStatsServicePromise: Promise.resolve(usageStatsService), }); - usageData = await collector.fetch(getMockFetchContext(defaultCallClusterMock)); + const esClient = getMockedEsClient(defaultEsClientSearchMock); + + usageData = await collector.fetch(getMockFetchContext(esClient)); }); test('sets enabled to true', () => { diff --git a/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts b/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts index 44388453d0707..269490bddd8dc 100644 --- a/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts +++ b/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts @@ -4,19 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LegacyCallAPIOptions } from 'src/core/server'; +import { ElasticsearchClient } from 'src/core/server'; import { take } from 'rxjs/operators'; import { CollectorFetchContext, UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { Observable } from 'rxjs'; import { PluginsSetup } from '../plugin'; import { UsageStats, UsageStatsServiceSetup } from '../usage_stats'; -type CallCluster = ( - endpoint: string, - clientParams: Record, - options?: LegacyCallAPIOptions -) => Promise; - interface SpacesAggregationResponse { hits: { total: { value: number }; @@ -37,7 +31,7 @@ interface SpacesAggregationResponse { * @return {UsageData} */ async function getSpacesUsage( - callCluster: CallCluster, + esClient: ElasticsearchClient, kibanaIndex: string, features: PluginsSetup['features'], spacesAvailable: boolean @@ -50,7 +44,7 @@ async function getSpacesUsage( let resp: SpacesAggregationResponse | undefined; try { - resp = await callCluster('search', { + ({ body: resp } = await esClient.search({ index: kibanaIndex, body: { track_total_hits: true, @@ -72,7 +66,7 @@ async function getSpacesUsage( }, size: 0, }, - }); + })); } catch (err) { if (err.status === 404) { return null; @@ -208,14 +202,14 @@ export function getSpacesUsageCollector( 'apiCalls.resolveCopySavedObjectsErrors.createNewCopiesEnabled.yes': { type: 'long' }, 'apiCalls.resolveCopySavedObjectsErrors.createNewCopiesEnabled.no': { type: 'long' }, }, - fetch: async ({ callCluster }: CollectorFetchContext) => { + fetch: async ({ esClient }: CollectorFetchContext) => { const { licensing, kibanaIndexConfig$, features, usageStatsServicePromise } = deps; const license = await licensing.license$.pipe(take(1)).toPromise(); const available = license.isAvailable; // some form of spaces is available for all valid licenses const kibanaIndex = (await kibanaIndexConfig$.pipe(take(1)).toPromise()).kibana.index; - const usageData = await getSpacesUsage(callCluster, kibanaIndex, features, available); + const usageData = await getSpacesUsage(esClient, kibanaIndex, features, available); const usageStats = await getUsageStats(usageStatsServicePromise, available); return { diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/types.ts b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/types.ts index 89252f7c90104..d1f64c9298f15 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/types.ts +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/types.ts @@ -4,9 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ +import { AlertTypeParams } from '../../../../alerts/common'; import { Query } from '../../../../../../src/plugins/data/common'; -export interface GeoContainmentAlertParams { +export interface GeoContainmentAlertParams extends AlertTypeParams { index: string; indexId: string; geoField: string; diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/types.ts b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/types.ts index 5ac9c7fd29317..3f487135f0474 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/types.ts +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/types.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { AlertTypeParams } from '../../../../alerts/common'; import { Query } from '../../../../../../src/plugins/data/common'; export enum TrackingEvent { @@ -12,7 +13,7 @@ export enum TrackingEvent { crossed = 'crossed', } -export interface GeoThresholdAlertParams { +export interface GeoThresholdAlertParams extends AlertTypeParams { index: string; indexId: string; geoField: string; diff --git a/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx b/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx index 3c84f2a5d4f9c..12e021958f497 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx @@ -71,6 +71,10 @@ interface KibanaDeps { http: HttpSetup; } +function isString(value: unknown): value is string { + return typeof value === 'string'; +} + export const IndexThresholdAlertTypeExpression: React.FunctionComponent< AlertTypeParamsExpressionProps > = ({ alertParams, alertInterval, setAlertParams, setAlertProperty, errors, charts, data }) => { @@ -190,10 +194,10 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent< }; })} onChange={async (selected: EuiComboBoxOptionOption[]) => { - setAlertParams( - 'index', - selected.map((aSelected) => aSelected.value) - ); + const indicies: string[] = selected + .map((aSelected) => aSelected.value) + .filter(isString); + setAlertParams('index', indicies); const indices = selected.map((s) => s.value as string); // reset time field and expression fields if indices are deleted diff --git a/x-pack/plugins/stack_alerts/public/alert_types/threshold/types.ts b/x-pack/plugins/stack_alerts/public/alert_types/threshold/types.ts index 356b0fbbc0845..4868b92feaeb9 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/threshold/types.ts +++ b/x-pack/plugins/stack_alerts/public/alert_types/threshold/types.ts @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import { AlertTypeParams } from '../../../../alerts/common'; + export interface Comparator { text: string; value: string; @@ -24,7 +26,7 @@ export interface GroupByType { validNormalizedTypes: string[]; } -export interface IndexThresholdAlertParams { +export interface IndexThresholdAlertParams extends AlertTypeParams { index: string[]; timeField?: string; aggType: string; diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/alert_type.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/alert_type.ts index 51d7361bfe762..0d8628d00df85 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/alert_type.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/alert_type.ts @@ -9,7 +9,13 @@ import { schema } from '@kbn/config-schema'; import { Logger } from 'src/core/server'; import { STACK_ALERTS_FEATURE_ID } from '../../../common'; import { getGeoContainmentExecutor } from './geo_containment'; -import { AlertType } from '../../../../alerts/server'; +import { + AlertType, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, + AlertTypeParams, +} from '../../../../alerts/server'; import { Query } from '../../../../../../src/plugins/data/common/query'; export const GEO_CONTAINMENT_ID = '.geo-containment'; @@ -96,7 +102,7 @@ export const ParamsSchema = schema.object({ boundaryIndexQuery: schema.maybe(schema.any({})), }); -export interface GeoContainmentParams { +export interface GeoContainmentParams extends AlertTypeParams { index: string; indexId: string; geoField: string; @@ -111,8 +117,34 @@ export interface GeoContainmentParams { indexQuery?: Query; boundaryIndexQuery?: Query; } +export interface GeoContainmentState extends AlertTypeState { + shapesFilters: Record; + shapesIdsNamesMap: Record; +} +export interface GeoContainmentInstanceState extends AlertInstanceState { + location: number[]; + shapeLocationId: string; + dateInShape: string | null; + docId: string; +} +export interface GeoContainmentInstanceContext extends AlertInstanceContext { + entityId: string; + entityDateTime: string | null; + entityDocumentId: string; + detectionDateTime: string; + entityLocation: string; + containingBoundaryId: string; + containingBoundaryName: unknown; +} -export function getAlertType(logger: Logger): AlertType { +export type GeoContainmentAlertType = AlertType< + GeoContainmentParams, + GeoContainmentState, + GeoContainmentInstanceState, + GeoContainmentInstanceContext +>; + +export function getAlertType(logger: Logger): GeoContainmentAlertType { const alertTypeName = i18n.translate('xpack.stackAlerts.geoContainment.alertTypeTitle', { defaultMessage: 'Tracking containment', }); @@ -128,6 +160,12 @@ export function getAlertType(logger: Logger): AlertType { id: GEO_CONTAINMENT_ID, name: alertTypeName, actionGroups: [{ id: ActionGroupId, name: actionGroupName }], + recoveryActionGroup: { + id: 'notGeoContained', + name: i18n.translate('xpack.stackAlerts.geoContainment.notGeoContained', { + defaultMessage: 'No longer contained', + }), + }, defaultActionGroupId: ActionGroupId, executor: getGeoContainmentExecutor(logger), producer: STACK_ALERTS_FEATURE_ID, diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/geo_containment.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/geo_containment.ts index ed951f340f8ed..612eff3014985 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/geo_containment.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/geo_containment.ts @@ -8,15 +8,16 @@ import _ from 'lodash'; import { SearchResponse } from 'elasticsearch'; import { Logger } from 'src/core/server'; import { executeEsQueryFactory, getShapesFilters, OTHER_CATEGORY } from './es_query_builder'; -import { AlertServices, AlertTypeState } from '../../../../alerts/server'; -import { ActionGroupId, GEO_CONTAINMENT_ID, GeoContainmentParams } from './alert_type'; +import { AlertServices } from '../../../../alerts/server'; +import { + ActionGroupId, + GEO_CONTAINMENT_ID, + GeoContainmentInstanceState, + GeoContainmentAlertType, + GeoContainmentInstanceContext, +} from './alert_type'; -export interface LatestEntityLocation { - location: number[]; - shapeLocationId: string; - dateInShape: string | null; - docId: string; -} +export type LatestEntityLocation = GeoContainmentInstanceState; // Flatten agg results and get latest locations for each entity export function transformResults( @@ -97,9 +98,10 @@ function getOffsetTime(delayOffsetWithUnits: string, oldTime: Date): Date { export function getActiveEntriesAndGenerateAlerts( prevLocationMap: Record, currLocationMap: Map, - alertInstanceFactory: ( - x: string - ) => { scheduleActions: (x: string, y: Record) => void }, + alertInstanceFactory: AlertServices< + GeoContainmentInstanceState, + GeoContainmentInstanceContext + >['alertInstanceFactory'], shapesIdsNamesMap: Record, currIntervalEndTime: Date ) { @@ -127,23 +129,8 @@ export function getActiveEntriesAndGenerateAlerts( }); return allActiveEntriesMap; } - -export const getGeoContainmentExecutor = (log: Logger) => - async function ({ - previousStartedAt, - startedAt, - services, - params, - alertId, - state, - }: { - previousStartedAt: Date | null; - startedAt: Date; - services: AlertServices; - params: GeoContainmentParams; - alertId: string; - state: AlertTypeState; - }): Promise { +export const getGeoContainmentExecutor = (log: Logger): GeoContainmentAlertType['executor'] => + async function ({ previousStartedAt, startedAt, services, params, alertId, state }) { const { shapesFilters, shapesIdsNamesMap } = state.shapesFilters ? state : await getShapesFilters( diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/index.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/index.ts index 02116d0701bfa..86e9e4fa3d672 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/index.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/index.ts @@ -6,7 +6,13 @@ import { Logger } from 'src/core/server'; import { AlertingSetup } from '../../types'; -import { GeoContainmentParams, getAlertType } from './alert_type'; +import { + GeoContainmentParams, + GeoContainmentState, + GeoContainmentInstanceState, + GeoContainmentInstanceContext, + getAlertType, +} from './alert_type'; interface RegisterParams { logger: Logger; @@ -15,5 +21,10 @@ interface RegisterParams { export function register(params: RegisterParams) { const { logger, alerts } = params; - alerts.registerType(getAlertType(logger)); + alerts.registerType< + GeoContainmentParams, + GeoContainmentState, + GeoContainmentInstanceState, + GeoContainmentInstanceContext + >(getAlertType(logger)); } diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/alert_type.test.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/alert_type.test.ts index 0592c944de570..98842c8cc2cba 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/alert_type.test.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/alert_type.test.ts @@ -18,6 +18,10 @@ describe('alertType', () => { expect(alertType.actionGroups).toEqual([ { id: 'Tracked entity contained', name: 'Tracking containment met' }, ]); + expect(alertType.recoveryActionGroup).toEqual({ + id: 'notGeoContained', + name: 'No longer contained', + }); expect(alertType.actionVariables).toMatchSnapshot(); }); diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/geo_containment.test.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/geo_containment.test.ts index 885081e859dd7..26b51060c2e73 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/geo_containment.test.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/geo_containment.test.ts @@ -10,6 +10,8 @@ import sampleJsonResponseWithNesting from './es_sample_response_with_nesting.jso import { getActiveEntriesAndGenerateAlerts, transformResults } from '../geo_containment'; import { SearchResponse } from 'elasticsearch'; import { OTHER_CATEGORY } from '../es_query_builder'; +import { alertsMock } from '../../../../../alerts/server/mocks'; +import { GeoContainmentInstanceContext, GeoContainmentInstanceState } from '../alert_type'; describe('geo_containment', () => { describe('transformResults', () => { @@ -191,8 +193,12 @@ describe('geo_containment', () => { const emptyShapesIdsNamesMap = {}; const alertInstanceFactory = (instanceId: string) => { - return { - scheduleActions: (actionGroupId: string, context: Record) => { + const alertInstance = alertsMock.createAlertInstanceFactory< + GeoContainmentInstanceState, + GeoContainmentInstanceContext + >(); + alertInstance.scheduleActions.mockImplementation( + (actionGroupId: string, context?: GeoContainmentInstanceContext) => { const contextKeys = Object.keys(expectedContext[0].context); const contextSubset = _.pickBy(context, (v, k) => contextKeys.includes(k)); testAlertActionArr.push({ @@ -200,9 +206,12 @@ describe('geo_containment', () => { instanceId, context: contextSubset, }); - }, - }; + return alertInstance; + } + ); + return alertInstance; }; + const currentDateTime = new Date(); it('should use currently active entities if no older entity entries', () => { diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/alert_type.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/alert_type.ts index bf5e2fe2289db..2eccf2ff96fb0 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/alert_type.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/alert_type.ts @@ -9,7 +9,13 @@ import { schema } from '@kbn/config-schema'; import { Logger } from 'src/core/server'; import { STACK_ALERTS_FEATURE_ID } from '../../../common'; import { getGeoThresholdExecutor } from './geo_threshold'; -import { AlertType } from '../../../../alerts/server'; +import { + AlertType, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, + AlertTypeParams, +} from '../../../../alerts/server'; import { Query } from '../../../../../../src/plugins/data/common/query'; export const GEO_THRESHOLD_ID = '.geo-threshold'; @@ -155,7 +161,7 @@ export const ParamsSchema = schema.object({ boundaryIndexQuery: schema.maybe(schema.any({})), }); -export interface GeoThresholdParams { +export interface GeoThresholdParams extends AlertTypeParams { index: string; indexId: string; geoField: string; @@ -171,8 +177,41 @@ export interface GeoThresholdParams { indexQuery?: Query; boundaryIndexQuery?: Query; } +export interface GeoThresholdState extends AlertTypeState { + shapesFilters: Record; + shapesIdsNamesMap: Record; + prevLocationArr: GeoThresholdInstanceState[]; +} +export interface GeoThresholdInstanceState extends AlertInstanceState { + location: number[]; + shapeLocationId: string; + entityName: string; + dateInShape: string | null; + docId: string; +} +export interface GeoThresholdInstanceContext extends AlertInstanceContext { + entityId: string; + timeOfDetection: number; + crossingLine: string; + toEntityLocation: string; + toEntityDateTime: string | null; + toEntityDocumentId: string; + toBoundaryId: string; + toBoundaryName: unknown; + fromEntityLocation: string; + fromEntityDateTime: string | null; + fromEntityDocumentId: string; + fromBoundaryId: string; + fromBoundaryName: unknown; +} -export function getAlertType(logger: Logger): AlertType { +export type GeoThresholdAlertType = AlertType< + GeoThresholdParams, + GeoThresholdState, + GeoThresholdInstanceState, + GeoThresholdInstanceContext +>; +export function getAlertType(logger: Logger): GeoThresholdAlertType { const alertTypeName = i18n.translate('xpack.stackAlerts.geoThreshold.alertTypeTitle', { defaultMessage: 'Tracking threshold', }); diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/geo_threshold.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/geo_threshold.ts index 5cb4156e84623..a2375537ae6e5 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/geo_threshold.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/geo_threshold.ts @@ -8,16 +8,14 @@ import _ from 'lodash'; import { SearchResponse } from 'elasticsearch'; import { Logger } from 'src/core/server'; import { executeEsQueryFactory, getShapesFilters, OTHER_CATEGORY } from './es_query_builder'; -import { AlertServices, AlertTypeState } from '../../../../alerts/server'; -import { ActionGroupId, GEO_THRESHOLD_ID, GeoThresholdParams } from './alert_type'; +import { + ActionGroupId, + GEO_THRESHOLD_ID, + GeoThresholdAlertType, + GeoThresholdInstanceState, +} from './alert_type'; -interface LatestEntityLocation { - location: number[]; - shapeLocationId: string; - entityName: string; - dateInShape: string | null; - docId: string; -} +export type LatestEntityLocation = GeoThresholdInstanceState; // Flatten agg results and get latest locations for each entity export function transformResults( @@ -172,22 +170,8 @@ function getOffsetTime(delayOffsetWithUnits: string, oldTime: Date): Date { return adjustedDate; } -export const getGeoThresholdExecutor = (log: Logger) => - async function ({ - previousStartedAt, - startedAt, - services, - params, - alertId, - state, - }: { - previousStartedAt: Date | null; - startedAt: Date; - services: AlertServices; - params: GeoThresholdParams; - alertId: string; - state: AlertTypeState; - }): Promise { +export const getGeoThresholdExecutor = (log: Logger): GeoThresholdAlertType['executor'] => + async function ({ previousStartedAt, startedAt, services, params, alertId, state }) { const { shapesFilters, shapesIdsNamesMap } = state.shapesFilters ? state : await getShapesFilters( diff --git a/x-pack/plugins/transform/public/app/constants/index.ts b/x-pack/plugins/transform/public/app/constants/index.ts index 299007d84768f..7e86c3af1e139 100644 --- a/x-pack/plugins/transform/public/app/constants/index.ts +++ b/x-pack/plugins/transform/public/app/constants/index.ts @@ -10,12 +10,6 @@ export enum SECTION_SLUG { CREATE_TRANSFORM = 'create_transform', } -export enum TRANSFORM_DOC_PATHS { - default = 'docs.html', - plugins = 'plugins.html', - transforms = 'transforms.html', -} - // UI Metric constants export const UIM_APP_NAME = 'transform'; export const UIM_TRANSFORM_LIST_LOAD = 'transform_list_load'; diff --git a/x-pack/plugins/transform/public/app/hooks/use_documentation_links.ts b/x-pack/plugins/transform/public/app/hooks/use_documentation_links.ts index 060b228390c39..31b7d15e92187 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_documentation_links.ts +++ b/x-pack/plugins/transform/public/app/hooks/use_documentation_links.ts @@ -6,20 +6,15 @@ import { useAppDependencies } from '../app_dependencies'; -import { TRANSFORM_DOC_PATHS } from '../constants'; - export const useDocumentationLinks = () => { const deps = useAppDependencies(); const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = deps.docLinks; return { - esDocBasePath: `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}/`, - esIndicesCreateIndex: `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}/indices-create-index.html#indices-create-index`, + esIndicesCreateIndex: deps.docLinks.links.apis.createIndex, esPluginDocBasePath: `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/plugins/${DOC_LINK_VERSION}/`, - esQueryDsl: `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}/query-dsl.html`, - esStackOverviewDocBasePath: `${ELASTIC_WEBSITE_URL}guide/en/elastic-stack-overview/${DOC_LINK_VERSION}/`, - esTransform: `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}/${TRANSFORM_DOC_PATHS.transforms}`, - esTransformPivot: `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}/put-transform.html#put-transform-request-body`, - esTransformUpdate: `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}/update-transform.html`, - mlDocBasePath: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/`, + esQueryDsl: deps.docLinks.links.query.queryDsl, + esTransform: deps.docLinks.links.transforms.guide, + esTransformPivot: deps.docLinks.links.apis.createTransformRequest, + esTransformUpdate: deps.docLinks.links.apis.updateTransform, }; }; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index e5b82a5d3fcbc..116a81361699f 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -247,15 +247,8 @@ "charts.colormaps.greysText": "グレー", "charts.colormaps.redsText": "赤", "charts.colormaps.yellowToRedText": "黄色から赤", - "charts.controls.colorRanges.errorText": "各範囲は前の範囲よりも大きくなければなりません。", - "charts.controls.colorSchema.colorSchemaLabel": "配色", - "charts.controls.colorSchema.howToChangeColorsDescription": "それぞれの色は凡例で変更できます。", - "charts.controls.colorSchema.resetColorsButtonLabel": "色をリセット", - "charts.controls.colorSchema.reverseColorSchemaLabel": "図表を反転", - "charts.controls.rangeErrorMessage": "値は{min}と{max}の間でなければなりません", - "charts.controls.vislibBasicOptions.legendPositionLabel": "凡例位置", - "charts.controls.vislibBasicOptions.showTooltipLabel": "ツールヒントを表示", "charts.colorPicker.setColor.screenReaderDescription": "値 {legendDataLabel} の色を設定", + "charts.countText": "カウント", "console.autocomplete.addMethodMetaText": "メソド", "console.consoleDisplayName": "コンソール", "console.consoleMenu.copyAsCurlMessage": "リクエストが URL としてコピーされました", @@ -1357,6 +1350,8 @@ "data.search.functions.kibana_context.savedSearchId.help": "クエリとフィルターに使用する保存検索ID を指定します。", "data.search.functions.kibana_context.timeRange.help": "Kibana 時間範囲フィルターを指定します", "data.search.functions.kibana.help": "Kibana グローバルコンテキストを取得します", + "data.triggers.applyFilterDescription": "Kibanaフィルターが適用されるとき。単一の値または範囲フィルターにすることができます。", + "data.triggers.applyFilterTitle": "フィルターを適用", "devTools.badge.readOnly.text": "読み込み専用", "devTools.badge.readOnly.tooltip": "を保存できませんでした", "devTools.devToolsTitle": "開発ツール", @@ -1582,6 +1577,10 @@ "embeddableApi.samples.contactCard.displayName": "連絡先カード", "embeddableApi.samples.filterableContainer.displayName": "フィルター可能なダッシュボード", "embeddableApi.samples.filterableEmbeddable.displayName": "フィルター可能", + "embeddableApi.selectRangeTrigger.description": "ビジュアライゼーションでの値の範囲", + "embeddableApi.selectRangeTrigger.title": "範囲選択", + "embeddableApi.valueClickTrigger.description": "ビジュアライゼーションでデータポイントをクリック", + "embeddableApi.valueClickTrigger.title": "シングルクリック", "esUi.cronEditor.cronDaily.fieldHour.textAtLabel": "に", "esUi.cronEditor.cronDaily.fieldTimeLabel": "時間", "esUi.cronEditor.cronDaily.hourSelectLabel": "時間", @@ -3490,12 +3489,6 @@ "uiActions.actionPanel.more": "詳細", "uiActions.actionPanel.title": "オプション", "uiActions.errors.incompatibleAction": "操作に互換性がありません", - "data.triggers.applyFilterDescription": "Kibanaフィルターが適用されるとき。単一の値または範囲フィルターにすることができます。", - "data.triggers.applyFilterTitle": "フィルターを適用", - "embeddableApi.selectRangeTrigger.description": "ビジュアライゼーションでの値の範囲", - "embeddableApi.selectRangeTrigger.title": "範囲選択", - "embeddableApi.valueClickTrigger.description": "ビジュアライゼーションでデータポイントをクリック", - "embeddableApi.valueClickTrigger.title": "シングルクリック", "usageCollection.stats.notReadyMessage": "まだ統計が準備できていません。しばらくたってから再試行してください。", "visDefaultEditor.advancedToggle.advancedLinkLabel": "高度な設定", "visDefaultEditor.agg.toggleEditorButtonAriaLabel": "{schema} エディターを切り替える", @@ -3615,6 +3608,14 @@ "visDefaultEditor.editorConfig.dateHistogram.customInterval.helpText": "構成間隔の倍数でなければなりません: {interval}", "visDefaultEditor.editorConfig.histogram.interval.helpText": "構成間隔の倍数でなければなりません: {interval}", "visDefaultEditor.metrics.wrongLastBucketTypeErrorMessage": "「{type}」メトリック集約を使用する場合、最後のバケット集約は「Date Histogram」または「Histogram」でなければなりません。", + "visDefaultEditor.options.colorRanges.errorText": "各範囲は前の範囲よりも大きくなければなりません。", + "visDefaultEditor.options.colorSchema.colorSchemaLabel": "配色", + "visDefaultEditor.options.colorSchema.howToChangeColorsDescription": "それぞれの色は凡例で変更できます。", + "visDefaultEditor.options.colorSchema.resetColorsButtonLabel": "色をリセット", + "visDefaultEditor.options.colorSchema.reverseColorSchemaLabel": "図表を反転", + "visDefaultEditor.options.rangeErrorMessage": "値は{min}と{max}の間でなければなりません", + "visDefaultEditor.options.vislibBasicOptions.legendPositionLabel": "凡例位置", + "visDefaultEditor.options.vislibBasicOptions.showTooltipLabel": "ツールヒントを表示", "visDefaultEditor.sidebar.autoApplyChangesOffLabel": "自動適用がオフです", "visDefaultEditor.sidebar.autoApplyChangesOnLabel": "自動適用がオンです", "visDefaultEditor.sidebar.autoApplyChangesTooltip": "変更されるごとにビジュアライゼーションを自動的に更新します。", @@ -4305,27 +4306,6 @@ "visTypeVislib.advancedSettings.visualization.heatmap.maxBucketsText": "1つのデータソースが返せるバケットの最大数です。値が大きいとブラウザのレンダリング速度が下がる可能性があります。", "visTypeVislib.advancedSettings.visualization.heatmap.maxBucketsTitle": "ヒートマップの最大バケット数", "visTypeVislib.aggResponse.allDocsTitle": "すべてのドキュメント", - "visTypeXy.area.areaTitle": "エリア", - "charts.countText": "カウント", - "visTypeXy.area.groupTitle": "系列を分割", - "visTypeXy.area.metricsTitle": "Y 軸", - "visTypeXy.area.radiusTitle": "点のサイズ", - "visTypeXy.area.segmentTitle": "X 軸", - "visTypeXy.area.splitTitle": "チャートを分割", - "visTypeXy.area.tabs.metricsAxesTitle": "メトリックと軸", - "visTypeXy.area.tabs.panelSettingsTitle": "パネル設定", - "visTypeXy.axisModes.normalText": "標準", - "visTypeXy.axisModes.percentageText": "割合 (%)", - "visTypeXy.axisModes.silhouetteText": "シルエット", - "visTypeXy.axisModes.wiggleText": "振動", - "visTypeXy.categoryAxis.rotate.angledText": "傾斜", - "visTypeXy.categoryAxis.rotate.horizontalText": "横", - "visTypeXy.categoryAxis.rotate.verticalText": "縦", - "visTypeXy.chartModes.normalText": "標準", - "visTypeXy.chartModes.stackedText": "スタック", - "visTypeXy.chartTypes.areaText": "エリア", - "visTypeXy.chartTypes.barText": "バー", - "visTypeXy.chartTypes.lineText": "折れ線", "visTypeVislib.controls.gaugeOptions.alignmentLabel": "アラインメント", "visTypeVislib.controls.gaugeOptions.autoExtendRangeLabel": "範囲を自動拡張", "visTypeVislib.controls.gaugeOptions.displayWarningsLabel": "警告を表示", @@ -4351,6 +4331,68 @@ "visTypeVislib.controls.heatmapOptions.scaleToDataBoundsLabel": "データバウンドに合わせる", "visTypeVislib.controls.heatmapOptions.showLabelsTitle": "ラベルを表示", "visTypeVislib.controls.heatmapOptions.useCustomRangesLabel": "カスタム範囲を使用", + "visTypeVislib.editors.heatmap.basicSettingsTitle": "基本設定", + "visTypeVislib.editors.heatmap.heatmapSettingsTitle": "ヒートマップ設定", + "visTypeVislib.editors.heatmap.highlightLabel": "ハイライト範囲", + "visTypeVislib.editors.heatmap.highlightLabelTooltip": "チャートのカーソルを当てた部分と凡例の対応するラベルをハイライトします。", + "visTypeVislib.editors.pie.donutLabel": "ドーナッツ", + "visTypeVislib.editors.pie.labelsSettingsTitle": "ラベル設定", + "visTypeVislib.editors.pie.pieSettingsTitle": "パイ設定", + "visTypeVislib.editors.pie.showLabelsLabel": "ラベルを表示", + "visTypeVislib.editors.pie.showTopLevelOnlyLabel": "トップレベルのみ表示", + "visTypeVislib.editors.pie.showValuesLabel": "値を表示", + "visTypeVislib.functions.pie.help": "パイビジュアライゼーション", + "visTypeVislib.functions.vislib.help": "Vislib ビジュアライゼーション", + "visTypeVislib.gauge.alignmentAutomaticTitle": "自動", + "visTypeVislib.gauge.alignmentHorizontalTitle": "横", + "visTypeVislib.gauge.alignmentVerticalTitle": "縦", + "visTypeVislib.gauge.gaugeTitle": "ゲージ", + "visTypeVislib.gauge.gaugeTypes.arcText": "弧形", + "visTypeVislib.gauge.gaugeTypes.circleText": "円", + "visTypeVislib.gauge.groupTitle": "グループを分割", + "visTypeVislib.gauge.metricTitle": "メトリック", + "visTypeVislib.goal.goalTitle": "ゴール", + "visTypeVislib.goal.groupTitle": "グループを分割", + "visTypeVislib.goal.metricTitle": "メトリック", + "visTypeVislib.heatmap.groupTitle": "Y 軸", + "visTypeVislib.heatmap.metricTitle": "値", + "visTypeVislib.heatmap.segmentTitle": "X 軸", + "visTypeVislib.heatmap.splitTitle": "チャートを分割", + "visTypeVislib.pie.metricTitle": "サイズのスライス", + "visTypeVislib.pie.pieTitle": "パイ", + "visTypeVislib.pie.segmentTitle": "スライスの分割", + "visTypeVislib.pie.splitTitle": "チャートを分割", + "visTypeVislib.vislib.errors.noResultsFoundTitle": "結果が見つかりませんでした", + "visTypeVislib.vislib.heatmap.maxBucketsText": "定義された数列が多すぎます ({nr})。構成されている最大値は {max} です。", + "visTypeVislib.vislib.legend.filterForValueButtonAriaLabel": "値 {legendDataLabel} でフィルタリング", + "visTypeVislib.vislib.legend.filterOptionsLegend": "{legendDataLabel}、フィルターオプション", + "visTypeVislib.vislib.legend.filterOutValueButtonAriaLabel": "値 {legendDataLabel} を除外", + "visTypeVislib.vislib.legend.loadingLabel": "読み込み中…", + "visTypeVislib.vislib.legend.toggleLegendButtonAriaLabel": "凡例を切り替える", + "visTypeVislib.vislib.legend.toggleLegendButtonTitle": "凡例を切り替える", + "visTypeVislib.vislib.legend.toggleOptionsButtonAriaLabel": "{legendDataLabel}、トグルオプション", + "visTypeVislib.vislib.tooltip.fieldLabel": "フィールド", + "visTypeVislib.vislib.tooltip.valueLabel": "値", + "visTypeXy.area.areaTitle": "エリア", + "visTypeXy.area.groupTitle": "系列を分割", + "visTypeXy.area.metricsTitle": "Y 軸", + "visTypeXy.area.radiusTitle": "点のサイズ", + "visTypeXy.area.segmentTitle": "X 軸", + "visTypeXy.area.splitTitle": "チャートを分割", + "visTypeXy.area.tabs.metricsAxesTitle": "メトリックと軸", + "visTypeXy.area.tabs.panelSettingsTitle": "パネル設定", + "visTypeXy.axisModes.normalText": "標準", + "visTypeXy.axisModes.percentageText": "割合 (%)", + "visTypeXy.axisModes.silhouetteText": "シルエット", + "visTypeXy.axisModes.wiggleText": "振動", + "visTypeXy.categoryAxis.rotate.angledText": "傾斜", + "visTypeXy.categoryAxis.rotate.horizontalText": "横", + "visTypeXy.categoryAxis.rotate.verticalText": "縦", + "visTypeXy.chartModes.normalText": "標準", + "visTypeXy.chartModes.stackedText": "スタック", + "visTypeXy.chartTypes.areaText": "エリア", + "visTypeXy.chartTypes.barText": "バー", + "visTypeXy.chartTypes.lineText": "折れ線", "visTypeXy.controls.pointSeries.categoryAxis.alignLabel": "配置", "visTypeXy.controls.pointSeries.categoryAxis.filterLabelsLabel": "フィルターラベル", "visTypeXy.controls.pointSeries.categoryAxis.labelsTitle": "ラベル", @@ -4393,16 +4435,6 @@ "visTypeXy.controls.pointSeries.valueAxes.toggleOptionsAriaLabel": "{axisName} オプションを切り替える", "visTypeXy.controls.pointSeries.valueAxes.yAxisTitle": "Y 軸", "visTypeXy.controls.truncateLabel": "切り捨て", - "visTypeVislib.editors.heatmap.basicSettingsTitle": "基本設定", - "visTypeVislib.editors.heatmap.heatmapSettingsTitle": "ヒートマップ設定", - "visTypeVislib.editors.heatmap.highlightLabel": "ハイライト範囲", - "visTypeVislib.editors.heatmap.highlightLabelTooltip": "チャートのカーソルを当てた部分と凡例の対応するラベルをハイライトします。", - "visTypeVislib.editors.pie.donutLabel": "ドーナッツ", - "visTypeVislib.editors.pie.labelsSettingsTitle": "ラベル設定", - "visTypeVislib.editors.pie.pieSettingsTitle": "パイ設定", - "visTypeVislib.editors.pie.showLabelsLabel": "ラベルを表示", - "visTypeVislib.editors.pie.showTopLevelOnlyLabel": "トップレベルのみ表示", - "visTypeVislib.editors.pie.showValuesLabel": "値を表示", "visTypeXy.editors.pointSeries.currentTimeMarkerLabel": "現在時刻マーカー", "visTypeXy.editors.pointSeries.orderBucketsBySumLabel": "バケットを合計で並べ替え", "visTypeXy.editors.pointSeries.settingsTitle": "設定", @@ -4413,23 +4445,6 @@ "visTypeXy.editors.pointSeries.thresholdLine.valueLabel": "しきい値", "visTypeXy.editors.pointSeries.thresholdLine.widthLabel": "線の幅", "visTypeXy.editors.pointSeries.thresholdLineSettingsTitle": "しきい線", - "visTypeVislib.functions.pie.help": "パイビジュアライゼーション", - "visTypeVislib.functions.vislib.help": "Vislib ビジュアライゼーション", - "visTypeVislib.gauge.alignmentAutomaticTitle": "自動", - "visTypeVislib.gauge.alignmentHorizontalTitle": "横", - "visTypeVislib.gauge.alignmentVerticalTitle": "縦", - "visTypeVislib.gauge.gaugeTitle": "ゲージ", - "visTypeVislib.gauge.gaugeTypes.arcText": "弧形", - "visTypeVislib.gauge.gaugeTypes.circleText": "円", - "visTypeVislib.gauge.groupTitle": "グループを分割", - "visTypeVislib.gauge.metricTitle": "メトリック", - "visTypeVislib.goal.goalTitle": "ゴール", - "visTypeVislib.goal.groupTitle": "グループを分割", - "visTypeVislib.goal.metricTitle": "メトリック", - "visTypeVislib.heatmap.groupTitle": "Y 軸", - "visTypeVislib.heatmap.metricTitle": "値", - "visTypeVislib.heatmap.segmentTitle": "X 軸", - "visTypeVislib.heatmap.splitTitle": "チャートを分割", "visTypeXy.histogram.groupTitle": "系列を分割", "visTypeXy.histogram.metricTitle": "Y 軸", "visTypeXy.histogram.radiusTitle": "点のサイズ", @@ -4453,27 +4468,12 @@ "visTypeXy.line.radiusTitle": "点のサイズ", "visTypeXy.line.segmentTitle": "X 軸", "visTypeXy.line.splitTitle": "チャートを分割", - "visTypeVislib.pie.metricTitle": "サイズのスライス", - "visTypeVislib.pie.pieTitle": "パイ", - "visTypeVislib.pie.segmentTitle": "スライスの分割", - "visTypeVislib.pie.splitTitle": "チャートを分割", "visTypeXy.scaleTypes.linearText": "線形", "visTypeXy.scaleTypes.logText": "ログ", "visTypeXy.scaleTypes.squareRootText": "平方根", "visTypeXy.thresholdLine.style.dashedText": "鎖線", "visTypeXy.thresholdLine.style.dotdashedText": "点線", "visTypeXy.thresholdLine.style.fullText": "完全", - "visTypeVislib.vislib.errors.noResultsFoundTitle": "結果が見つかりませんでした", - "visTypeVislib.vislib.heatmap.maxBucketsText": "定義された数列が多すぎます ({nr})。構成されている最大値は {max} です。", - "visTypeVislib.vislib.legend.filterForValueButtonAriaLabel": "値 {legendDataLabel} でフィルタリング", - "visTypeVislib.vislib.legend.filterOptionsLegend": "{legendDataLabel}、フィルターオプション", - "visTypeVislib.vislib.legend.filterOutValueButtonAriaLabel": "値 {legendDataLabel} を除外", - "visTypeVislib.vislib.legend.loadingLabel": "読み込み中…", - "visTypeVislib.vislib.legend.toggleLegendButtonAriaLabel": "凡例を切り替える", - "visTypeVislib.vislib.legend.toggleLegendButtonTitle": "凡例を切り替える", - "visTypeVislib.vislib.legend.toggleOptionsButtonAriaLabel": "{legendDataLabel}、トグルオプション", - "visTypeVislib.vislib.tooltip.fieldLabel": "フィールド", - "visTypeVislib.vislib.tooltip.valueLabel": "値", "visualizations.advancedSettings.visualizeEnableLabsText": "ユーザーが実験的なビジュアライゼーションを作成、表示、編集できるようになります。無効の場合、\n ユーザーは本番準備が整ったビジュアライゼーションのみを利用できます。", "visualizations.advancedSettings.visualizeEnableLabsTitle": "実験的なビジュアライゼーションを有効にする", "visualizations.disabledLabVisualizationMessage": "ラボビジュアライゼーションを表示するには、高度な設定でラボモードをオンにしてください。", @@ -7153,7 +7153,6 @@ "xpack.fleet.agentPolicy.confirmModalConfirmButtonLabel": "変更を保存してデプロイ", "xpack.fleet.agentPolicy.confirmModalDescription": "このアクションは元に戻せません。続行していいですか?", "xpack.fleet.agentPolicy.confirmModalTitle": "変更を保存してデプロイ", - "xpack.fleet.agentPolicy.linkedAgentCountText": "{count, plural, one {#件のエージェント} other {#件のエージェント}}", "xpack.fleet.agentPolicyActionMenu.buttonText": "アクション", "xpack.fleet.agentPolicyActionMenu.copyPolicyActionText": "ポリシーをコピー", "xpack.fleet.agentPolicyActionMenu.enrollAgentActionText": "エージェントの追加", @@ -10908,11 +10907,6 @@ "xpack.logstash.managementSection.notPossibleToManagePipelinesMessage": "現在ライセンス情報が利用できないため Logstash パイプラインを使用できません。", "xpack.logstash.managementSection.pipelineCrudOperationsNotAllowedDescription": "ご使用の {licenseType} ライセンスは期限切れのため、Logstash パイプラインの編集、作成、削除ができません。", "xpack.logstash.managementSection.pipelinesTitle": "Logstashパイプライン", - "xpack.logstash.manualUpgradeButtonLabel": "再試行", - "xpack.logstash.newPipelineMessage": "パイプラインを追加する前に、構成をアップグレードする必要があります。", - "xpack.logstash.notManualUpgradeButtonLabel": "アップグレード", - "xpack.logstash.notManualUpgradeTitle": "アップグレードの時がやってきました!", - "xpack.logstash.notNewPipelineMessage": "このパイプラインを編集する前に、構成をアップグレードする必要があります。", "xpack.logstash.pipelineBatchDelayTooltip": "パイプラインイベントバッチを作成する際、それぞれのイベントでパイプラインワーカーにサイズの小さなバッチを送る前に何ミリ秒間待つかです。\n\nデフォルト値:50ms", "xpack.logstash.pipelineBatchSizeTooltip": "フィルターとアウトプットをを実行する前に各ワーカースレッドがインプットから収集するイベントの最低数です。基本的にバッチサイズが大きくなるほど効率が上がりますが、メモリーオーバーヘッドも大きくなります。このオプションを効率的に使用するには、LS_HEAP_SIZE 変数を設定して JVM のヒープサイズを増やす必要があるかもしれません。\n\nデフォルト値:125", "xpack.logstash.pipelineEditor.cancelButtonLabel": "キャンセル", @@ -10971,8 +10965,6 @@ "xpack.logstash.units.megabytesLabel": "メガバイト", "xpack.logstash.units.petabytesLabel": "ペタバイト", "xpack.logstash.units.terabytesLabel": "テラバイト", - "xpack.logstash.upgradeFailedTitle": "アップグレード失敗", - "xpack.logstash.upgradeFailureActions.goBackButtonLabel": "戻る", "xpack.logstash.upstreamPipelineArgumentMustContainAnIdPropertyErrorMessage": "upstreamPipeline 引数には id プロパティを含める必要があります", "xpack.logstash.workersTooltip": "パイプラインのフィルターとアウトプットステージを同時に実行するワーカーの数です。イベントが詰まってしまう場合や CPU が飽和状態ではない場合は、マシンの処理能力をより有効に活用するため、この数字を上げてみてください。\n\nデフォルト値:ホストの CPU コア数です", "xpack.maps.actionSelect.label": "アクション", @@ -17111,36 +17103,12 @@ "xpack.securitySolution.endpoint.policyDetailsConfig.windows.events.security": "セキュリティ", "xpack.securitySolution.endpoint.policyDetailType": "タイプ", "xpack.securitySolution.endpoint.policyList.actionButtonText": "Endpoint Securityを追加", - "xpack.securitySolution.endpoint.policyList.actionMenu": "開く", - "xpack.securitySolution.endpoint.policyList.agentPolicyAction": "エージェントポリシーを表示", - "xpack.securitySolution.endpoint.policyList.createdAt": "作成日時", - "xpack.securitySolution.endpoint.policyList.createdBy": "作成者", - "xpack.securitySolution.endpoint.policyList.createNewButton": "新しいポリシーを作成", - "xpack.securitySolution.endpoint.policyList.deleteConfirm.cancelButtonTitle": "キャンセル", - "xpack.securitySolution.endpoint.policyList.deleteConfirm.confirmDeleteButton": "ポリシーを削除", - "xpack.securitySolution.endpoint.policyList.deleteConfirm.deletingButton": "削除中…", - "xpack.securitySolution.endpoint.policyList.deleteConfirm.message": "この操作は元に戻すことができません。続行していいですか?", - "xpack.securitySolution.endpoint.policyList.deleteConfirm.title": "ポリシーを削除し、変更をデプロイ", - "xpack.securitySolution.endpoint.policyList.deleteConfirm.warningMessage": "このポリシーを削除すると、これらのホストからEndpoint Securityが削除されます", - "xpack.securitySolution.endpoint.policyList.deleteConfirm.warningTitle": "このアクションにより、{hostCount, plural, one {個のホスト} other {個のホスト}}からEndpoint Securityが削除されます", - "xpack.securitySolution.endpoint.policyList.deleteFailedToast": "失敗しました。", - "xpack.securitySolution.endpoint.policyList.deleteFailedToastBody": "ポリシーを削除できませんでした", - "xpack.securitySolution.endpoint.policyList.deleteSuccessToast": "成功!", - "xpack.securitySolution.endpoint.policyList.deleteSuccessToastDetails": "ポリシーが削除されました。", "xpack.securitySolution.endpoint.policyList.emptyCreateNewButton": "エージェントの登録", - "xpack.securitySolution.endpoint.policyList.nameField": "ポリシー名", "xpack.securitySolution.endpoint.policyList.onboardingDocsLink": "セキュリティアプリドキュメントを表示", "xpack.securitySolution.endpoint.policyList.onboardingSectionOne": "Endpoint Securityでは、脅威防御、検出、深いセキュリティデータの可視化を実現し、ホストを保護します。", "xpack.securitySolution.endpoint.policyList.onboardingSectionThree": "開始するには、Endpoint Security統合をエージェントに追加します。詳細については、 ", "xpack.securitySolution.endpoint.policyList.onboardingSectionTwo": "このページでは、Endpoint Securityを実行している環境でホストを表示して管理できます。", "xpack.securitySolution.endpoint.policyList.onboardingTitle": "Endpoint Securityの基本", - "xpack.securitySolution.endpoint.policyList.policyDeleteAction": "ポリシーを削除", - "xpack.securitySolution.endpoint.policyList.revision": "rev. {revNumber}", - "xpack.securitySolution.endpoint.policyList.updatedAt": "最終更新", - "xpack.securitySolution.endpoint.policyList.updatedBy": "最終更新者", - "xpack.securitySolution.endpoint.policyList.versionField": "v{version}", - "xpack.securitySolution.endpoint.policyList.versionFieldLabel": "バージョン", - "xpack.securitySolution.endpoint.policyList.viewTitleTotalCount": "{totalItemCount, plural, one {# ポリシー} other {# ポリシー}}", "xpack.securitySolution.endpoint.policyResponse.backLinkTitle": "エンドポイント詳細", "xpack.securitySolution.endpoint.policyResponse.title": "ポリシー応答", "xpack.securitySolution.endpoint.resolver.eitherLineageLimitExceeded": "以下のビジュアライゼーションとイベントリストの一部のプロセスイベントを表示できませんでした。データの上限に達しました。", @@ -17778,8 +17746,6 @@ "xpack.securitySolution.paginatedTable.tooManyResultsToastText": "クエリ範囲を縮めて結果をさらにフィルタリングしてください", "xpack.securitySolution.paginatedTable.tooManyResultsToastTitle": " - 結果が多すぎます", "xpack.securitySolution.policiesTab": "ポリシー", - "xpack.securitySolution.policyList.pageSubTitle": "保護の表示と構成", - "xpack.securitySolution.policyList.pageTitle": "ポリシー", "xpack.securitySolution.policyStatusText.failure": "失敗", "xpack.securitySolution.policyStatusText.success": "成功", "xpack.securitySolution.policyStatusText.warning": "警告", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 879418870b527..f03b720c8a77f 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -247,15 +247,8 @@ "charts.colormaps.greysText": "灰色", "charts.colormaps.redsText": "红色", "charts.colormaps.yellowToRedText": "黄到红", - "charts.controls.colorRanges.errorText": "每个范围应大于前一范围。", - "charts.controls.colorSchema.colorSchemaLabel": "颜色方案", - "charts.controls.colorSchema.howToChangeColorsDescription": "可以更改图例中的各个颜色。", - "charts.controls.colorSchema.resetColorsButtonLabel": "重置颜色", - "charts.controls.colorSchema.reverseColorSchemaLabel": "反向方案", - "charts.controls.rangeErrorMessage": "值必须是在 {min} 到 {max} 的范围内", - "charts.controls.vislibBasicOptions.legendPositionLabel": "图例位置", - "charts.controls.vislibBasicOptions.showTooltipLabel": "显示工具提示", "charts.colorPicker.setColor.screenReaderDescription": "为值 {legendDataLabel} 设置颜色", + "charts.countText": "计数", "console.autocomplete.addMethodMetaText": "方法", "console.consoleDisplayName": "控制台", "console.consoleMenu.copyAsCurlMessage": "请求已复制为 cURL", @@ -1358,6 +1351,8 @@ "data.search.functions.kibana_context.savedSearchId.help": "指定要用于查询和筛选的已保存搜索 ID", "data.search.functions.kibana_context.timeRange.help": "指定 Kibana 时间范围筛选", "data.search.functions.kibana.help": "获取 kibana 全局上下文", + "data.triggers.applyFilterDescription": "应用 kibana 筛选时。可能是单个值或范围筛选。", + "data.triggers.applyFilterTitle": "应用筛选", "devTools.badge.readOnly.text": "只读", "devTools.badge.readOnly.tooltip": "无法保存", "devTools.devToolsTitle": "开发工具", @@ -1583,6 +1578,10 @@ "embeddableApi.samples.contactCard.displayName": "联系卡片", "embeddableApi.samples.filterableContainer.displayName": "可筛选仪表板", "embeddableApi.samples.filterableEmbeddable.displayName": "可筛选", + "embeddableApi.selectRangeTrigger.description": "可视化上的一组值", + "embeddableApi.selectRangeTrigger.title": "范围选择", + "embeddableApi.valueClickTrigger.description": "可视化上的数据点单击", + "embeddableApi.valueClickTrigger.title": "单击", "esUi.cronEditor.cronDaily.fieldHour.textAtLabel": "在", "esUi.cronEditor.cronDaily.fieldTimeLabel": "时间", "esUi.cronEditor.cronDaily.hourSelectLabel": "小时", @@ -3491,12 +3490,6 @@ "uiActions.actionPanel.more": "更多", "uiActions.actionPanel.title": "选项", "uiActions.errors.incompatibleAction": "操作不兼容", - "data.triggers.applyFilterDescription": "应用 kibana 筛选时。可能是单个值或范围筛选。", - "data.triggers.applyFilterTitle": "应用筛选", - "embeddableApi.selectRangeTrigger.description": "可视化上的一组值", - "embeddableApi.selectRangeTrigger.title": "范围选择", - "embeddableApi.valueClickTrigger.description": "可视化上的数据点单击", - "embeddableApi.valueClickTrigger.title": "单击", "usageCollection.stats.notReadyMessage": "统计信息尚未准备就绪。请稍后重试。", "visDefaultEditor.advancedToggle.advancedLinkLabel": "高级", "visDefaultEditor.agg.toggleEditorButtonAriaLabel": "切换 {schema} 编辑器", @@ -3616,6 +3609,14 @@ "visDefaultEditor.editorConfig.dateHistogram.customInterval.helpText": "必须是配置时间间隔的倍数:{interval}", "visDefaultEditor.editorConfig.histogram.interval.helpText": "必须是配置时间间隔的倍数:{interval}", "visDefaultEditor.metrics.wrongLastBucketTypeErrorMessage": "使用“{type}”指标聚合时,上一存储桶聚合必须是“Date Histogram”或“Histogram”。", + "visDefaultEditor.options.colorRanges.errorText": "每个范围应大于前一范围。", + "visDefaultEditor.options.colorSchema.colorSchemaLabel": "颜色方案", + "visDefaultEditor.options.colorSchema.howToChangeColorsDescription": "可以更改图例中的各个颜色。", + "visDefaultEditor.options.colorSchema.resetColorsButtonLabel": "重置颜色", + "visDefaultEditor.options.colorSchema.reverseColorSchemaLabel": "反向方案", + "visDefaultEditor.options.rangeErrorMessage": "值必须是在 {min} 到 {max} 的范围内", + "visDefaultEditor.options.vislibBasicOptions.legendPositionLabel": "图例位置", + "visDefaultEditor.options.vislibBasicOptions.showTooltipLabel": "显示工具提示", "visDefaultEditor.sidebar.autoApplyChangesOffLabel": "自动应用关闭", "visDefaultEditor.sidebar.autoApplyChangesOnLabel": "自动应用开启", "visDefaultEditor.sidebar.autoApplyChangesTooltip": "每次更改时自动更新可视化。", @@ -4307,27 +4308,6 @@ "visTypeVislib.advancedSettings.visualization.heatmap.maxBucketsText": "单个数据源可以返回的最大存储桶数目。较高的数目可能对浏览器呈现性能有负面影响", "visTypeVislib.advancedSettings.visualization.heatmap.maxBucketsTitle": "热图最大存储桶数", "visTypeVislib.aggResponse.allDocsTitle": "所有文档", - "visTypeXy.area.areaTitle": "面积图", - "charts.countText": "计数", - "visTypeXy.area.groupTitle": "拆分序列", - "visTypeXy.area.metricsTitle": "Y 轴", - "visTypeXy.area.radiusTitle": "点大小", - "visTypeXy.area.segmentTitle": "X 轴", - "visTypeXy.area.splitTitle": "拆分图表", - "visTypeXy.area.tabs.metricsAxesTitle": "指标和轴", - "visTypeXy.area.tabs.panelSettingsTitle": "面板设置", - "visTypeXy.axisModes.normalText": "正常", - "visTypeXy.axisModes.percentageText": "百分比", - "visTypeXy.axisModes.silhouetteText": "剪影", - "visTypeXy.axisModes.wiggleText": "扭动", - "visTypeXy.categoryAxis.rotate.angledText": "带角度", - "visTypeXy.categoryAxis.rotate.horizontalText": "水平", - "visTypeXy.categoryAxis.rotate.verticalText": "垂直", - "visTypeXy.chartModes.normalText": "正常", - "visTypeXy.chartModes.stackedText": "堆叠", - "visTypeXy.chartTypes.areaText": "面积图", - "visTypeXy.chartTypes.barText": "条形图", - "visTypeXy.chartTypes.lineText": "折线图", "visTypeVislib.controls.gaugeOptions.alignmentLabel": "对齐方式", "visTypeVislib.controls.gaugeOptions.autoExtendRangeLabel": "自动扩展范围", "visTypeVislib.controls.gaugeOptions.displayWarningsLabel": "显示警告", @@ -4353,6 +4333,68 @@ "visTypeVislib.controls.heatmapOptions.scaleToDataBoundsLabel": "缩放到数据边界", "visTypeVislib.controls.heatmapOptions.showLabelsTitle": "显示标签", "visTypeVislib.controls.heatmapOptions.useCustomRangesLabel": "使用定制范围", + "visTypeVislib.editors.heatmap.basicSettingsTitle": "基本设置", + "visTypeVislib.editors.heatmap.heatmapSettingsTitle": "热图设置", + "visTypeVislib.editors.heatmap.highlightLabel": "高亮范围", + "visTypeVislib.editors.heatmap.highlightLabelTooltip": "高亮显示图表中鼠标悬停的范围以及图例中对应的标签。", + "visTypeVislib.editors.pie.donutLabel": "圆环图", + "visTypeVislib.editors.pie.labelsSettingsTitle": "标签设置", + "visTypeVislib.editors.pie.pieSettingsTitle": "饼图设置", + "visTypeVislib.editors.pie.showLabelsLabel": "显示标签", + "visTypeVislib.editors.pie.showTopLevelOnlyLabel": "仅显示顶级", + "visTypeVislib.editors.pie.showValuesLabel": "显示值", + "visTypeVislib.functions.pie.help": "饼图可视化", + "visTypeVislib.functions.vislib.help": "Vislib 可视化", + "visTypeVislib.gauge.alignmentAutomaticTitle": "自动", + "visTypeVislib.gauge.alignmentHorizontalTitle": "水平", + "visTypeVislib.gauge.alignmentVerticalTitle": "垂直", + "visTypeVislib.gauge.gaugeTitle": "仪表盘图", + "visTypeVislib.gauge.gaugeTypes.arcText": "弧形", + "visTypeVislib.gauge.gaugeTypes.circleText": "圆形", + "visTypeVislib.gauge.groupTitle": "拆分组", + "visTypeVislib.gauge.metricTitle": "指标", + "visTypeVislib.goal.goalTitle": "目标图", + "visTypeVislib.goal.groupTitle": "拆分组", + "visTypeVislib.goal.metricTitle": "指标", + "visTypeVislib.heatmap.groupTitle": "Y 轴", + "visTypeVislib.heatmap.metricTitle": "值", + "visTypeVislib.heatmap.segmentTitle": "X 轴", + "visTypeVislib.heatmap.splitTitle": "拆分图表", + "visTypeVislib.pie.metricTitle": "切片大小", + "visTypeVislib.pie.pieTitle": "饼图", + "visTypeVislib.pie.segmentTitle": "拆分切片", + "visTypeVislib.pie.splitTitle": "拆分图表", + "visTypeVislib.vislib.errors.noResultsFoundTitle": "找不到结果", + "visTypeVislib.vislib.heatmap.maxBucketsText": "定义了过多的序列 ({nr})。配置的最大值为 {max}。", + "visTypeVislib.vislib.legend.filterForValueButtonAriaLabel": "筛留值 {legendDataLabel}", + "visTypeVislib.vislib.legend.filterOptionsLegend": "{legendDataLabel}, 筛选选项", + "visTypeVislib.vislib.legend.filterOutValueButtonAriaLabel": "筛除值 {legendDataLabel}", + "visTypeVislib.vislib.legend.loadingLabel": "正在加载……", + "visTypeVislib.vislib.legend.toggleLegendButtonAriaLabel": "切换图例", + "visTypeVislib.vislib.legend.toggleLegendButtonTitle": "切换图例", + "visTypeVislib.vislib.legend.toggleOptionsButtonAriaLabel": "{legendDataLabel}, 切换选项", + "visTypeVislib.vislib.tooltip.fieldLabel": "字段", + "visTypeVislib.vislib.tooltip.valueLabel": "值", + "visTypeXy.area.areaTitle": "面积图", + "visTypeXy.area.groupTitle": "拆分序列", + "visTypeXy.area.metricsTitle": "Y 轴", + "visTypeXy.area.radiusTitle": "点大小", + "visTypeXy.area.segmentTitle": "X 轴", + "visTypeXy.area.splitTitle": "拆分图表", + "visTypeXy.area.tabs.metricsAxesTitle": "指标和轴", + "visTypeXy.area.tabs.panelSettingsTitle": "面板设置", + "visTypeXy.axisModes.normalText": "正常", + "visTypeXy.axisModes.percentageText": "百分比", + "visTypeXy.axisModes.silhouetteText": "剪影", + "visTypeXy.axisModes.wiggleText": "扭动", + "visTypeXy.categoryAxis.rotate.angledText": "带角度", + "visTypeXy.categoryAxis.rotate.horizontalText": "水平", + "visTypeXy.categoryAxis.rotate.verticalText": "垂直", + "visTypeXy.chartModes.normalText": "正常", + "visTypeXy.chartModes.stackedText": "堆叠", + "visTypeXy.chartTypes.areaText": "面积图", + "visTypeXy.chartTypes.barText": "条形图", + "visTypeXy.chartTypes.lineText": "折线图", "visTypeXy.controls.pointSeries.categoryAxis.alignLabel": "对齐", "visTypeXy.controls.pointSeries.categoryAxis.filterLabelsLabel": "筛选标签", "visTypeXy.controls.pointSeries.categoryAxis.labelsTitle": "标签", @@ -4395,16 +4437,6 @@ "visTypeXy.controls.pointSeries.valueAxes.toggleOptionsAriaLabel": "切换 {axisName} 选项", "visTypeXy.controls.pointSeries.valueAxes.yAxisTitle": "Y 轴", "visTypeXy.controls.truncateLabel": "截断", - "visTypeVislib.editors.heatmap.basicSettingsTitle": "基本设置", - "visTypeVislib.editors.heatmap.heatmapSettingsTitle": "热图设置", - "visTypeVislib.editors.heatmap.highlightLabel": "高亮范围", - "visTypeVislib.editors.heatmap.highlightLabelTooltip": "高亮显示图表中鼠标悬停的范围以及图例中对应的标签。", - "visTypeVislib.editors.pie.donutLabel": "圆环图", - "visTypeVislib.editors.pie.labelsSettingsTitle": "标签设置", - "visTypeVislib.editors.pie.pieSettingsTitle": "饼图设置", - "visTypeVislib.editors.pie.showLabelsLabel": "显示标签", - "visTypeVislib.editors.pie.showTopLevelOnlyLabel": "仅显示顶级", - "visTypeVislib.editors.pie.showValuesLabel": "显示值", "visTypeXy.editors.pointSeries.currentTimeMarkerLabel": "当前时间标记", "visTypeXy.editors.pointSeries.orderBucketsBySumLabel": "按总计值排序存储桶", "visTypeXy.editors.pointSeries.settingsTitle": "设置", @@ -4415,23 +4447,6 @@ "visTypeXy.editors.pointSeries.thresholdLine.valueLabel": "阈值", "visTypeXy.editors.pointSeries.thresholdLine.widthLabel": "线条宽度", "visTypeXy.editors.pointSeries.thresholdLineSettingsTitle": "阈值线条", - "visTypeVislib.functions.pie.help": "饼图可视化", - "visTypeVislib.functions.vislib.help": "Vislib 可视化", - "visTypeVislib.gauge.alignmentAutomaticTitle": "自动", - "visTypeVislib.gauge.alignmentHorizontalTitle": "水平", - "visTypeVislib.gauge.alignmentVerticalTitle": "垂直", - "visTypeVislib.gauge.gaugeTitle": "仪表盘图", - "visTypeVislib.gauge.gaugeTypes.arcText": "弧形", - "visTypeVislib.gauge.gaugeTypes.circleText": "圆形", - "visTypeVislib.gauge.groupTitle": "拆分组", - "visTypeVislib.gauge.metricTitle": "指标", - "visTypeVislib.goal.goalTitle": "目标图", - "visTypeVislib.goal.groupTitle": "拆分组", - "visTypeVislib.goal.metricTitle": "指标", - "visTypeVislib.heatmap.groupTitle": "Y 轴", - "visTypeVislib.heatmap.metricTitle": "值", - "visTypeVislib.heatmap.segmentTitle": "X 轴", - "visTypeVislib.heatmap.splitTitle": "拆分图表", "visTypeXy.histogram.groupTitle": "拆分序列", "visTypeXy.histogram.metricTitle": "Y 轴", "visTypeXy.histogram.radiusTitle": "点大小", @@ -4455,27 +4470,12 @@ "visTypeXy.line.radiusTitle": "点大小", "visTypeXy.line.segmentTitle": "X 轴", "visTypeXy.line.splitTitle": "拆分图表", - "visTypeVislib.pie.metricTitle": "切片大小", - "visTypeVislib.pie.pieTitle": "饼图", - "visTypeVislib.pie.segmentTitle": "拆分切片", - "visTypeVislib.pie.splitTitle": "拆分图表", "visTypeXy.scaleTypes.linearText": "线性", "visTypeXy.scaleTypes.logText": "对数", "visTypeXy.scaleTypes.squareRootText": "平方根", "visTypeXy.thresholdLine.style.dashedText": "虚线", "visTypeXy.thresholdLine.style.dotdashedText": "点虚线", "visTypeXy.thresholdLine.style.fullText": "实线", - "visTypeVislib.vislib.errors.noResultsFoundTitle": "找不到结果", - "visTypeVislib.vislib.heatmap.maxBucketsText": "定义了过多的序列 ({nr})。配置的最大值为 {max}。", - "visTypeVislib.vislib.legend.filterForValueButtonAriaLabel": "筛留值 {legendDataLabel}", - "visTypeVislib.vislib.legend.filterOptionsLegend": "{legendDataLabel}, 筛选选项", - "visTypeVislib.vislib.legend.filterOutValueButtonAriaLabel": "筛除值 {legendDataLabel}", - "visTypeVislib.vislib.legend.loadingLabel": "正在加载……", - "visTypeVislib.vislib.legend.toggleLegendButtonAriaLabel": "切换图例", - "visTypeVislib.vislib.legend.toggleLegendButtonTitle": "切换图例", - "visTypeVislib.vislib.legend.toggleOptionsButtonAriaLabel": "{legendDataLabel}, 切换选项", - "visTypeVislib.vislib.tooltip.fieldLabel": "字段", - "visTypeVislib.vislib.tooltip.valueLabel": "值", "visualizations.advancedSettings.visualizeEnableLabsText": "允许用户创建、查看和编辑实验性可视化。如果禁用,\n 仅被视为生产就绪的可视化可供用户使用。", "visualizations.advancedSettings.visualizeEnableLabsTitle": "启用实验性可视化", "visualizations.disabledLabVisualizationMessage": "请在高级设置中打开实验室模式,以查看实验室可视化。", @@ -7160,7 +7160,6 @@ "xpack.fleet.agentPolicy.confirmModalConfirmButtonLabel": "保存并部署更改", "xpack.fleet.agentPolicy.confirmModalDescription": "此操作无法撤消。是否确定要继续?", "xpack.fleet.agentPolicy.confirmModalTitle": "保存并部署更改", - "xpack.fleet.agentPolicy.linkedAgentCountText": "{count, plural, one {# 个代理} other {# 个代理}}", "xpack.fleet.agentPolicyActionMenu.buttonText": "操作", "xpack.fleet.agentPolicyActionMenu.copyPolicyActionText": "复制策略", "xpack.fleet.agentPolicyActionMenu.enrollAgentActionText": "添加代理", @@ -10921,11 +10920,6 @@ "xpack.logstash.managementSection.notPossibleToManagePipelinesMessage": "您不能管理 Logstash 管道,因为许可信息当前不可用。", "xpack.logstash.managementSection.pipelineCrudOperationsNotAllowedDescription": "您不能编辑、创建或删除您的 Logstash 管道,因为您的{licenseType}许可已过期。", "xpack.logstash.managementSection.pipelinesTitle": "Logstash 管道", - "xpack.logstash.manualUpgradeButtonLabel": "重试", - "xpack.logstash.newPipelineMessage": "在您可以添加管道之前,我们需要升级您的配置。", - "xpack.logstash.notManualUpgradeButtonLabel": "升级", - "xpack.logstash.notManualUpgradeTitle": "是时候升级了!", - "xpack.logstash.notNewPipelineMessage": "在您可以编辑此管道之前,我们需要升级您的配置。", "xpack.logstash.pipelineBatchDelayTooltip": "创建管道事件批时,将过小的批分派给管道工作线程之前要等候每个事件的时长(毫秒)。\n\n默认值:50ms", "xpack.logstash.pipelineBatchSizeTooltip": "单个工作线程在尝试执行其筛选和输出之前可以从输入收集的最大事件数目。较大的批大小通常更有效,但代价是内存开销也较大。您可能需要通过设置 LS_HEAP_SIZE 变量来增大 JVM 堆大小,从而有效利用该选项。\n\n默认值:125", "xpack.logstash.pipelineEditor.cancelButtonLabel": "取消", @@ -10984,8 +10978,6 @@ "xpack.logstash.units.megabytesLabel": "兆字节", "xpack.logstash.units.petabytesLabel": "万兆字节", "xpack.logstash.units.terabytesLabel": "兆兆字节", - "xpack.logstash.upgradeFailedTitle": "升级失败", - "xpack.logstash.upgradeFailureActions.goBackButtonLabel": "返回", "xpack.logstash.upstreamPipelineArgumentMustContainAnIdPropertyErrorMessage": "upstreamPipeline 参数必须包含 id 属性", "xpack.logstash.workersTooltip": "并行执行管道的筛选和输出阶段的工作线程数目。如果您发现事件出现积压或 CPU 未饱和,请考虑增大此数值,以更好地利用机器处理能力。\n\n默认值:主机的 CPU 核心数", "xpack.maps.actionSelect.label": "操作", @@ -17129,36 +17121,12 @@ "xpack.securitySolution.endpoint.policyDetailsConfig.windows.events.security": "安全", "xpack.securitySolution.endpoint.policyDetailType": "类型", "xpack.securitySolution.endpoint.policyList.actionButtonText": "添加 Endpoint Security", - "xpack.securitySolution.endpoint.policyList.actionMenu": "打开", - "xpack.securitySolution.endpoint.policyList.agentPolicyAction": "查看代理策略", - "xpack.securitySolution.endpoint.policyList.createdAt": "创建日期", - "xpack.securitySolution.endpoint.policyList.createdBy": "创建者", - "xpack.securitySolution.endpoint.policyList.createNewButton": "创建新策略", - "xpack.securitySolution.endpoint.policyList.deleteConfirm.cancelButtonTitle": "取消", - "xpack.securitySolution.endpoint.policyList.deleteConfirm.confirmDeleteButton": "删除策略", - "xpack.securitySolution.endpoint.policyList.deleteConfirm.deletingButton": "正在删除……", - "xpack.securitySolution.endpoint.policyList.deleteConfirm.message": "此操作无法撤消。是否确定要继续?", - "xpack.securitySolution.endpoint.policyList.deleteConfirm.title": "删除策略并部署更改", - "xpack.securitySolution.endpoint.policyList.deleteConfirm.warningMessage": "删除此策略将从这些主机上移除 Endpoint Security", - "xpack.securitySolution.endpoint.policyList.deleteConfirm.warningTitle": "此操作将从 {hostCount, plural, one {# 个主机} other {# 个主机}}上删除 Endpoint Security", - "xpack.securitySolution.endpoint.policyList.deleteFailedToast": "失败!", - "xpack.securitySolution.endpoint.policyList.deleteFailedToastBody": "无法删除策略", - "xpack.securitySolution.endpoint.policyList.deleteSuccessToast": "成功!", - "xpack.securitySolution.endpoint.policyList.deleteSuccessToastDetails": "策略已删除。", "xpack.securitySolution.endpoint.policyList.emptyCreateNewButton": "注册代理", - "xpack.securitySolution.endpoint.policyList.nameField": "策略名称", "xpack.securitySolution.endpoint.policyList.onboardingDocsLink": "查看 Security 应用文档", "xpack.securitySolution.endpoint.policyList.onboardingSectionOne": "Endpoint Security 使用威胁防御、检测和深度安全数据可见性来保护您的主机。", "xpack.securitySolution.endpoint.policyList.onboardingSectionThree": "首先,将 Endpoint Security 集成添加到您的代理。有关更多信息, ", "xpack.securitySolution.endpoint.policyList.onboardingSectionTwo": "从此页面,您将可以查看和管理环境中运行 Endpoint Security 的主机。", "xpack.securitySolution.endpoint.policyList.onboardingTitle": "开始使用 Endpoint Security", - "xpack.securitySolution.endpoint.policyList.policyDeleteAction": "删除策略", - "xpack.securitySolution.endpoint.policyList.revision": "修订 {revNumber}", - "xpack.securitySolution.endpoint.policyList.updatedAt": "最后更新时间", - "xpack.securitySolution.endpoint.policyList.updatedBy": "最后更新者", - "xpack.securitySolution.endpoint.policyList.versionField": "v{version}", - "xpack.securitySolution.endpoint.policyList.versionFieldLabel": "版本", - "xpack.securitySolution.endpoint.policyList.viewTitleTotalCount": "{totalItemCount, plural, one {# 个策略} other {# 个策略}}", "xpack.securitySolution.endpoint.policyResponse.backLinkTitle": "终端详情", "xpack.securitySolution.endpoint.policyResponse.title": "策略响应", "xpack.securitySolution.endpoint.resolver.eitherLineageLimitExceeded": "下面可视化和事件列表中的一些进程事件无法显示,因为已达到数据限制。", @@ -17796,8 +17764,6 @@ "xpack.securitySolution.paginatedTable.tooManyResultsToastText": "缩减您的查询范围,以更好地筛选结果", "xpack.securitySolution.paginatedTable.tooManyResultsToastTitle": " - 结果过多", "xpack.securitySolution.policiesTab": "策略", - "xpack.securitySolution.policyList.pageSubTitle": "查看并配置防护", - "xpack.securitySolution.policyList.pageTitle": "策略", "xpack.securitySolution.policyStatusText.failure": "失败", "xpack.securitySolution.policyStatusText.success": "成功", "xpack.securitySolution.policyStatusText.warning": "警告", diff --git a/x-pack/plugins/triggers_actions_ui/README.md b/x-pack/plugins/triggers_actions_ui/README.md index 629ab944ac623..37f605b0d500f 100644 --- a/x-pack/plugins/triggers_actions_ui/README.md +++ b/x-pack/plugins/triggers_actions_ui/README.md @@ -1116,7 +1116,7 @@ Each action type should be defined as an `ActionTypeModel` object with the follo validateConnector: (connector: any) => ValidationResult; validateParams: (actionParams: any) => ValidationResult; actionConnectorFields: React.FunctionComponent | null; - actionParamsFields: any; + actionParamsFields: React.LazyExoticComponent>>; ``` |Property|Description| |---|---| diff --git a/x-pack/plugins/triggers_actions_ui/public/application/action_type_registry.mock.ts b/x-pack/plugins/triggers_actions_ui/public/application/action_type_registry.mock.ts index 06a10cb7f8fa8..3bc854cfaa311 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/action_type_registry.mock.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/action_type_registry.mock.ts @@ -4,7 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ActionTypeRegistryContract } from '../types'; +import React, { lazy, Fragment } from 'react'; +import uuid from 'uuid'; +import { ActionTypeModel, ActionTypeRegistryContract } from '../types'; const createActionTypeRegistryMock = () => { const mocked: jest.Mocked = { @@ -16,6 +18,27 @@ const createActionTypeRegistryMock = () => { return mocked; }; +const mockedActionParamsFields = lazy(async () => ({ + default() { + return React.createElement(Fragment); + }, +})); + +const createMockActionTypeModel = (actionType: Partial = {}): ActionTypeModel => { + const id = uuid.v4(); + return { + id, + iconClass: `iconClass-${id}`, + selectMessage: `selectMessage-${id}`, + validateConnector: jest.fn(), + validateParams: jest.fn(), + actionConnectorFields: null, + actionParamsFields: mockedActionParamsFields, + ...actionType, + }; +}; + export const actionTypeRegistryMock = { create: createActionTypeRegistryMock, + createMockActionTypeModel, }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts index 538c6be89ab4b..ea654bb21e88b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Alert, AlertType } from '../../types'; +import { Alert, AlertType, AlertUpdates } from '../../types'; import { httpServiceMock } from '../../../../../../src/core/public/mocks'; import { createAlert, @@ -538,7 +538,7 @@ describe('deleteAlerts', () => { describe('createAlert', () => { test('should call create alert API', async () => { - const alertToCreate = { + const alertToCreate: AlertUpdates = { name: 'test', consumer: 'alerts', tags: ['foo'], @@ -553,10 +553,13 @@ describe('createAlert', () => { notifyWhen: 'onActionGroupChange' as AlertNotifyWhenType, createdAt: new Date('1970-01-01T00:00:00.000Z'), updatedAt: new Date('1970-01-01T00:00:00.000Z'), - apiKey: null, apiKeyOwner: null, + createdBy: null, + updatedBy: null, + muteAll: false, + mutedInstanceIds: [], }; - const resolvedValue: Alert = { + const resolvedValue = { ...alertToCreate, id: '123', createdBy: null, @@ -576,7 +579,7 @@ describe('createAlert', () => { Array [ "/api/alerts/alert", Object { - "body": "{\\"name\\":\\"test\\",\\"consumer\\":\\"alerts\\",\\"tags\\":[\\"foo\\"],\\"enabled\\":true,\\"alertTypeId\\":\\"test\\",\\"schedule\\":{\\"interval\\":\\"1m\\"},\\"actions\\":[],\\"params\\":{},\\"throttle\\":null,\\"notifyWhen\\":\\"onActionGroupChange\\",\\"createdAt\\":\\"1970-01-01T00:00:00.000Z\\",\\"updatedAt\\":\\"1970-01-01T00:00:00.000Z\\",\\"apiKey\\":null,\\"apiKeyOwner\\":null}", + "body": "{\\"name\\":\\"test\\",\\"consumer\\":\\"alerts\\",\\"tags\\":[\\"foo\\"],\\"enabled\\":true,\\"alertTypeId\\":\\"test\\",\\"schedule\\":{\\"interval\\":\\"1m\\"},\\"actions\\":[],\\"params\\":{},\\"throttle\\":null,\\"notifyWhen\\":\\"onActionGroupChange\\",\\"createdAt\\":\\"1970-01-01T00:00:00.000Z\\",\\"updatedAt\\":\\"1970-01-01T00:00:00.000Z\\",\\"apiKeyOwner\\":null,\\"createdBy\\":null,\\"updatedBy\\":null,\\"muteAll\\":false,\\"mutedInstanceIds\\":[]}", }, ] `); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.test.tsx index f6164b1856bb5..b0e0bce413bac 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.test.tsx @@ -13,7 +13,7 @@ jest.mock('../../../common/lib/kibana'); describe('action_connector_form', () => { it('renders action_connector_form', () => { - const actionType = { + const actionType = actionTypeRegistryMock.createMockActionTypeModel({ id: 'my-action-type', iconClass: 'test', selectMessage: 'test', @@ -24,9 +24,7 @@ describe('action_connector_form', () => { const validationResult = { errors: {} }; return validationResult; }, - actionConnectorFields: null, - actionParamsFields: null, - }; + }); actionTypeRegistry.get.mockReturnValue(actionType); actionTypeRegistry.has.mockReturnValue(true); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx index d7d508cbcf121..60e86e32fdaa5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx @@ -115,20 +115,6 @@ describe('action_form', () => { actionParamsFields: mockedActionParamsFields, }; - const actionTypeWithoutParams = { - id: 'my-action-type-without-params', - iconClass: 'test', - selectMessage: 'test', - validateConnector: (): ValidationResult => { - return { errors: {} }; - }, - validateParams: (): ValidationResult => { - const validationResult = { errors: {} }; - return validationResult; - }, - actionConnectorFields: null, - actionParamsFields: null, - }; const useKibanaMock = useKibana as jest.Mocked; describe('action_form in alert', () => { @@ -207,7 +193,6 @@ describe('action_form', () => { disabledByLicenseActionType, disabledByActionType, preconfiguredOnly, - actionTypeWithoutParams, ]); actionTypeRegistry.has.mockReturnValue(true); actionTypeRegistry.get.mockReturnValue(actionType); @@ -327,14 +312,6 @@ describe('action_form', () => { enabledInLicense: true, minimumLicenseRequired: 'basic', }, - { - id: actionTypeWithoutParams.id, - name: 'Action type without params', - enabled: true, - enabledInConfig: true, - enabledInLicense: true, - minimumLicenseRequired: 'basic', - }, ]} /> ); @@ -537,14 +514,6 @@ describe('action_form', () => { ).toBeTruthy(); }); - it(`shouldn't render action types without params component`, async () => { - const wrapper = await setup(); - const actionOption = wrapper.find( - `[data-test-subj="${actionTypeWithoutParams.id}-ActionTypeSelectOption"]` - ); - expect(actionOption.exists()).toBeFalsy(); - }); - it('recognizes actions with broken connectors', async () => { await setup([ { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_menu.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_menu.test.tsx index 2343ea1036ed9..8cf0a18d328a7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_menu.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_menu.test.tsx @@ -34,7 +34,7 @@ describe('connector_add_flyout', () => { it('renders action type menu with proper EuiCards for registered action types', () => { const onActionTypeChange = jest.fn(); - const actionType = { + const actionType = actionTypeRegistryMock.createMockActionTypeModel({ id: 'my-action-type', iconClass: 'test', selectMessage: 'test', @@ -46,8 +46,7 @@ describe('connector_add_flyout', () => { return validationResult; }, actionConnectorFields: null, - actionParamsFields: null, - }; + }); actionTypeRegistry.get.mockReturnValueOnce(actionType); const wrapper = mountWithIntl( @@ -72,7 +71,7 @@ describe('connector_add_flyout', () => { it(`doesn't renders action types that are disabled via config`, () => { const onActionTypeChange = jest.fn(); - const actionType = { + const actionType = actionTypeRegistryMock.createMockActionTypeModel({ id: 'my-action-type', iconClass: 'test', selectMessage: 'test', @@ -84,8 +83,7 @@ describe('connector_add_flyout', () => { return validationResult; }, actionConnectorFields: null, - actionParamsFields: null, - }; + }); actionTypeRegistry.get.mockReturnValueOnce(actionType); const wrapper = mountWithIntl( @@ -110,7 +108,7 @@ describe('connector_add_flyout', () => { it(`renders action types as disabled when disabled by license`, () => { const onActionTypeChange = jest.fn(); - const actionType = { + const actionType = actionTypeRegistryMock.createMockActionTypeModel({ id: 'my-action-type', iconClass: 'test', selectMessage: 'test', @@ -122,8 +120,7 @@ describe('connector_add_flyout', () => { return validationResult; }, actionConnectorFields: null, - actionParamsFields: null, - }; + }); actionTypeRegistry.get.mockReturnValueOnce(actionType); const wrapper = mountWithIntl( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.test.tsx index 4ffb97f019152..92bf16c231eeb 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.test.tsx @@ -192,7 +192,7 @@ describe('connector_add_flyout', () => { let count = 0; function createActionType() { - return { + return actionTypeRegistryMock.createMockActionTypeModel({ id: `my-action-type-${++count}`, iconClass: 'test', selectMessage: 'test', @@ -204,6 +204,5 @@ function createActionType() { return validationResult; }, actionConnectorFields: null, - actionParamsFields: null, - }; + }); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.test.tsx index 31e4fdd4f4507..34ce00b4ef539 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.test.tsx @@ -33,7 +33,7 @@ describe('connector_add_modal', () => { }; }); it('renders connector modal form if addModalVisible is true', () => { - const actionTypeModel = { + const actionTypeModel = actionTypeRegistryMock.createMockActionTypeModel({ id: 'my-action-type', iconClass: 'test', selectMessage: 'test', @@ -45,8 +45,7 @@ describe('connector_add_modal', () => { return validationResult; }, actionConnectorFields: null, - actionParamsFields: null, - }; + }); actionTypeRegistry.get.mockReturnValueOnce(actionTypeModel); actionTypeRegistry.has.mockReturnValue(true); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.test.tsx index 581db16e9a136..6f82fb54a19f9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.test.tsx @@ -45,7 +45,7 @@ describe('connector_edit_flyout', () => { config: {}, }; - const actionType = { + const actionType = actionTypeRegistryMock.createMockActionTypeModel({ id: 'test-action-type-id', iconClass: 'test', selectMessage: 'test', @@ -57,8 +57,7 @@ describe('connector_edit_flyout', () => { return validationResult; }, actionConnectorFields: null, - actionParamsFields: null, - }; + }); actionTypeRegistry.get.mockReturnValue(actionType); actionTypeRegistry.has.mockReturnValue(true); useKibanaMock().services.actionTypeRegistry = actionTypeRegistry; @@ -90,7 +89,7 @@ describe('connector_edit_flyout', () => { config: {}, }; - const actionType = { + const actionType = actionTypeRegistryMock.createMockActionTypeModel({ id: 'test-action-type-id', iconClass: 'test', selectMessage: 'test', @@ -102,8 +101,7 @@ describe('connector_edit_flyout', () => { return validationResult; }, actionConnectorFields: null, - actionParamsFields: null, - }; + }); actionTypeRegistry.get.mockReturnValue(actionType); actionTypeRegistry.has.mockReturnValue(true); useKibanaMock().services.actionTypeRegistry = actionTypeRegistry; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx index 6057d2669f04c..c95d0a45670bf 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx @@ -103,7 +103,7 @@ describe('alert_add', () => { requiresAppContext: false, }; - const actionTypeModel = { + const actionTypeModel = actionTypeRegistryMock.createMockActionTypeModel({ id: 'my-action-type', iconClass: 'test', selectMessage: 'test', @@ -115,8 +115,7 @@ describe('alert_add', () => { return validationResult; }, actionConnectorFields: null, - actionParamsFields: null, - }; + }); actionTypeRegistry.get.mockReturnValueOnce(actionTypeModel); actionTypeRegistry.has.mockReturnValue(true); alertTypeRegistry.list.mockReturnValue([alertType]); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx index e5a6a8977a8c8..38a237b0143df 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx @@ -61,7 +61,7 @@ describe('alert_edit', () => { requiresAppContext: false, }; - const actionTypeModel = { + const actionTypeModel = actionTypeRegistryMock.createMockActionTypeModel({ id: 'my-action-type', iconClass: 'test', selectMessage: 'test', @@ -73,8 +73,7 @@ describe('alert_edit', () => { return validationResult; }, actionConnectorFields: null, - actionParamsFields: null, - }; + }); const alert: Alert = { id: 'ab5661e0-197e-45ee-b477-302d89193b5e', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.test.tsx index ef8d17d8c4c28..d02bf5697884c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.test.tsx @@ -35,7 +35,7 @@ describe('alert_form', () => { requiresAppContext: false, }; - const actionType = { + const actionType = actionTypeRegistryMock.createMockActionTypeModel({ id: 'my-action-type', iconClass: 'test', selectMessage: 'test', @@ -47,8 +47,7 @@ describe('alert_form', () => { return validationResult; }, actionConnectorFields: null, - actionParamsFields: null, - }; + }); const alertTypeNonEditable = { id: 'non-edit-alert-type', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/type_registry.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/type_registry.test.ts index aa61fcde9e9c2..55b56639aac38 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/type_registry.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/type_registry.test.ts @@ -6,6 +6,7 @@ import { TypeRegistry } from './type_registry'; import { ValidationResult, AlertTypeModel, ActionTypeModel } from '../types'; +import { actionTypeRegistryMock } from './action_type_registry.mock'; export const ExpressionComponent: React.FunctionComponent = () => { return null; @@ -30,7 +31,7 @@ const getTestActionType = ( iconClass?: string, selectedMessage?: string ): ActionTypeModel => { - return { + return actionTypeRegistryMock.createMockActionTypeModel({ id: id || 'my-action-type', iconClass: iconClass || 'test', selectMessage: selectedMessage || 'test', @@ -42,8 +43,7 @@ const getTestActionType = ( return validationResult; }, actionConnectorFields: null, - actionParamsFields: null, - }; + }); }; beforeEach(() => jest.resetAllMocks()); @@ -74,7 +74,12 @@ describe('get()', () => { expect(actionType).toMatchInlineSnapshot(` Object { "actionConnectorFields": null, - "actionParamsFields": null, + "actionParamsFields": Object { + "$$typeof": Symbol(react.lazy), + "_ctor": [Function], + "_result": null, + "_status": -1, + }, "iconClass": "test", "id": "my-action-type-snapshot", "selectMessage": "test", @@ -97,7 +102,8 @@ describe('get()', () => { describe('list()', () => { test('returns list of action types', () => { const actionTypeRegistry = new TypeRegistry(); - actionTypeRegistry.register(getTestActionType()); + const actionType = getTestActionType(); + actionTypeRegistry.register(actionType); const actionTypes = actionTypeRegistry.list(); expect(actionTypes).toEqual([ { @@ -105,7 +111,7 @@ describe('list()', () => { iconClass: 'test', selectMessage: 'test', actionConnectorFields: null, - actionParamsFields: null, + actionParamsFields: actionType.actionParamsFields, validateConnector: actionTypes[0].validateConnector, validateParams: actionTypes[0].validateParams, }, diff --git a/x-pack/plugins/triggers_actions_ui/public/plugin.ts b/x-pack/plugins/triggers_actions_ui/public/plugin.ts index f2c8957400fa5..05bfba27420c7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/plugin.ts +++ b/x-pack/plugins/triggers_actions_ui/public/plugin.ts @@ -35,12 +35,12 @@ import { AlertEditProps } from './application/sections/alert_form/alert_edit'; export interface TriggersAndActionsUIPublicPluginSetup { actionTypeRegistry: TypeRegistry; - alertTypeRegistry: TypeRegistry; + alertTypeRegistry: TypeRegistry>; } export interface TriggersAndActionsUIPublicPluginStart { actionTypeRegistry: TypeRegistry; - alertTypeRegistry: TypeRegistry; + alertTypeRegistry: TypeRegistry>; getAddConnectorFlyout: ( props: Omit ) => ReactElement; diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index 3fffe9fe230b4..599b820474a6b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -8,12 +8,12 @@ import type { DocLinksStart } from 'kibana/public'; import { ComponentType } from 'react'; import { ChartsPluginSetup } from 'src/plugins/charts/public'; import { DataPublicPluginStart } from 'src/plugins/data/public'; -import { ActionGroup, AlertActionParam } from '../../alerts/common'; +import { ActionGroup, AlertActionParam, AlertTypeParams } from '../../alerts/common'; import { ActionType } from '../../actions/common'; import { TypeRegistry } from './application/type_registry'; import { AlertType as CommonAlertType } from '../../alerts/common'; import { - SanitizedAlert as Alert, + SanitizedAlert, AlertAction, AlertAggregations, AlertTaskState, @@ -23,6 +23,11 @@ import { AlertingFrameworkHealth, AlertNotifyWhenType, } from '../../alerts/common'; + +// In Triggers and Actions we treat all `Alert`s as `SanitizedAlert` +// so the `Params` is a black-box of Record +type Alert = SanitizedAlert; + export { Alert, AlertAction, @@ -81,9 +86,7 @@ export interface ActionTypeModel> > > | null; - actionParamsFields: React.LazyExoticComponent< - ComponentType> - > | null; + actionParamsFields: React.LazyExoticComponent>>; } export interface ValidationResult { @@ -169,14 +172,17 @@ export interface AlertTableItem extends Alert { } export interface AlertTypeParamsExpressionProps< - AlertParamsType = unknown, + Params extends AlertTypeParams = AlertTypeParams, MetaData = Record > { - alertParams: AlertParamsType; + alertParams: Params; alertInterval: string; alertThrottle: string; - setAlertParams: (property: string, value: any) => void; - setAlertProperty: (key: Key, value: Alert[Key] | null) => void; + setAlertParams: (property: Key, value: Params[Key] | undefined) => void; + setAlertProperty: ( + key: Prop, + value: SanitizedAlert[Prop] | null + ) => void; errors: IErrorObject; defaultActionGroupId: string; actionGroups: ActionGroup[]; @@ -185,15 +191,15 @@ export interface AlertTypeParamsExpressionProps< data: DataPublicPluginStart; } -export interface AlertTypeModel { +export interface AlertTypeModel { id: string; description: string; iconClass: string; documentationUrl: string | ((docLinks: DocLinksStart) => string) | null; - validate: (alertParams: AlertParamsType) => ValidationResult; + validate: (alertParams: Params) => ValidationResult; alertParamsExpression: | React.FunctionComponent - | React.LazyExoticComponent>>; + | React.LazyExoticComponent>>; requiresAppContext: boolean; defaultActionMessage?: string; } diff --git a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/components/url_drilldown_collect_config/i18n.ts b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/components/url_drilldown_collect_config/i18n.ts index 500ef21b61dc4..001e16393b16b 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/components/url_drilldown_collect_config/i18n.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/components/url_drilldown_collect_config/i18n.ts @@ -34,7 +34,7 @@ export const txtAddVariableButtonTitle = i18n.translate( export const txtUrlTemplateLabel = i18n.translate( 'xpack.uiActionsEnhanced.drilldowns.urlDrilldownCollectConfig.urlTemplateLabel', { - defaultMessage: 'Enter URL template:', + defaultMessage: 'Enter URL:', } ); @@ -76,6 +76,27 @@ export const txtUrlTemplatePreviewLinkText = i18n.translate( export const txtUrlTemplateOpenInNewTab = i18n.translate( 'xpack.uiActionsEnhanced.drilldowns.urlDrilldownCollectConfig.openInNewTabLabel', { - defaultMessage: 'Open in new tab', + defaultMessage: 'Open in new window', + } +); + +export const txtUrlTemplateAdditionalOptions = i18n.translate( + 'xpack.uiActionsEnhanced.drilldowns.urlDrilldownCollectConfig.additionalOptions', + { + defaultMessage: 'Additional options', + } +); + +export const txtUrlTemplateEncodeUrl = i18n.translate( + 'xpack.uiActionsEnhanced.drilldowns.urlDrilldownCollectConfig.encodeUrl', + { + defaultMessage: 'Encode URL', + } +); + +export const txtUrlTemplateEncodeDescription = i18n.translate( + 'xpack.uiActionsEnhanced.drilldowns.urlDrilldownCollectConfig.encodeDescription', + { + defaultMessage: 'If enabled, URL will be escaped using percent encoding', } ); diff --git a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/components/url_drilldown_collect_config/url_drilldown_collect_config.tsx b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/components/url_drilldown_collect_config/url_drilldown_collect_config.tsx index eb8d01afbf420..04ed73b2ce0b8 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/components/url_drilldown_collect_config/url_drilldown_collect_config.tsx +++ b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/components/url_drilldown_collect_config/url_drilldown_collect_config.tsx @@ -6,7 +6,6 @@ import React, { useRef, useState } from 'react'; import { - EuiCheckbox, EuiFormRow, EuiIcon, EuiLink, @@ -17,6 +16,11 @@ import { EuiText, EuiTextArea, EuiSelectableOption, + EuiSwitch, + EuiAccordion, + EuiSpacer, + EuiPanel, + EuiTextColor, } from '@elastic/eui'; import { UrlDrilldownConfig } from '../../types'; import './index.scss'; @@ -28,6 +32,9 @@ import { txtUrlTemplateLabel, txtUrlTemplateOpenInNewTab, txtUrlTemplatePlaceholder, + txtUrlTemplateAdditionalOptions, + txtUrlTemplateEncodeUrl, + txtUrlTemplateEncodeDescription, } from './i18n'; export interface UrlDrilldownCollectConfig { @@ -110,15 +117,39 @@ export const UrlDrilldownCollectConfig: React.FC = ({ inputRef={textAreaRef} /> - - onConfig({ ...config, openInNewTab: !config.openInNewTab })} - /> - + + + + + + onConfig({ ...config, openInNewTab: !config.openInNewTab })} + /> + + + + {txtUrlTemplateEncodeUrl} + + {txtUrlTemplateEncodeDescription} + + } + checked={config.encodeUrl ?? true} + onChange={() => onConfig({ ...config, encodeUrl: !(config.encodeUrl ?? true) })} + /> + + + ); }; diff --git a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/types.ts b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/types.ts index fb7d96aaf8325..59942ab6ff940 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/types.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/types.ts @@ -7,6 +7,7 @@ export type UrlDrilldownConfig = { url: { format?: 'handlebars_v1'; template: string }; openInNewTab: boolean; + encodeUrl?: boolean; }; /** diff --git a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/url_template.test.ts b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/url_template.test.ts index 68a9654316d43..72c0a5ade7922 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/url_template.test.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/url_template.test.ts @@ -12,6 +12,18 @@ test('should compile url without variables', () => { expect(compile(url, {})).toBe(url); }); +test('by default, encodes URI', () => { + const url = 'https://elastic.co?foo=head%26shoulders'; + expect(compile(url, {})).not.toBe(url); + expect(compile(url, {})).toBe('https://elastic.co?foo=head%2526shoulders'); +}); + +test('when URI encoding is disabled, should not encode URI', () => { + const url = + 'https://xxxxx.service-now.com/nav_to.do?uri=incident.do%3Fsys_id%3D-1%26sysparm_query%3Dshort_description%3DHello'; + expect(compile(url, {}, false)).toBe(url); +}); + test('should fail on unknown syntax', () => { const url = 'https://elastic.co/{{}'; expect(() => compile(url, {})).toThrowError(); diff --git a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/url_template.ts b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/url_template.ts index f4a1acff8762b..7533920d07d52 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/url_template.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/url_template.ts @@ -9,6 +9,7 @@ import { encode, RisonValue } from 'rison-node'; import dateMath from '@elastic/datemath'; import moment, { Moment } from 'moment'; import numeral from '@elastic/numeral'; +import { url } from '../../../../../../src/plugins/kibana_utils/public'; const handlebars = createHandlebars(); @@ -116,7 +117,22 @@ handlebars.registerHelper('replace', (...args) => { return String(str).split(searchString).join(valueString); }); -export function compile(url: string, context: object): string { - const template = handlebars.compile(url, { strict: true, noEscape: true }); - return encodeURI(template(context)); +handlebars.registerHelper('encodeURIComponent', (component: unknown) => { + const str = String(component); + return encodeURIComponent(str); +}); +handlebars.registerHelper('encodeURIQuery', (component: unknown) => { + const str = String(component); + return url.encodeUriQuery(str); +}); + +export function compile(urlTemplate: string, context: object, doEncode: boolean = true): string { + const handlebarsTemplate = handlebars.compile(urlTemplate, { strict: true, noEscape: true }); + let processedUrl: string = handlebarsTemplate(context); + + if (doEncode) { + processedUrl = encodeURI(processedUrl); + } + + return processedUrl; } diff --git a/x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart.test.tsx.snap b/x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart.test.tsx.snap index 1a18cf5651bee..21d65f63783c5 100644 --- a/x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart.test.tsx.snap +++ b/x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart.test.tsx.snap @@ -490,8 +490,9 @@ exports[`DonutChart component renders a donut chart 1`] = ` - Up + Down - Down + Up - Up + Down - Down + Up - - - - -`; diff --git a/x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart_legend_row.test.tsx.snap b/x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart_legend_row.test.tsx.snap index bc6033ea7109a..6e2a58cf528ed 100644 --- a/x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart_legend_row.test.tsx.snap +++ b/x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart_legend_row.test.tsx.snap @@ -15,6 +15,7 @@ exports[`DonutChartLegendRow passes appropriate props 1`] = ` Foo diff --git a/x-pack/plugins/uptime/public/components/common/charts/__tests__/donut_chart_legend.test.tsx b/x-pack/plugins/uptime/public/components/common/charts/__tests__/donut_chart_legend.test.tsx index 1b71b87884fb8..2ef02106e6e66 100644 --- a/x-pack/plugins/uptime/public/components/common/charts/__tests__/donut_chart_legend.test.tsx +++ b/x-pack/plugins/uptime/public/components/common/charts/__tests__/donut_chart_legend.test.tsx @@ -3,14 +3,30 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import React from 'react'; +import { renderWithIntl } from '@kbn/test/jest'; import { DonutChartLegend } from '../donut_chart_legend'; -import { shallowWithIntl } from '@kbn/test/jest'; -import React from 'react'; + +import { STATUS_DOWN_LABEL, STATUS_UP_LABEL } from '../../translations'; describe('DonutChartLegend', () => { it('applies valid props as expected', () => { - const wrapper = shallowWithIntl(); - expect(wrapper).toMatchSnapshot(); + const up = 45; + const down = 23; + const component = renderWithIntl(); + + expect( + component.find('[data-test-subj="xpack.uptime.snapshot.donutChart.up.label"]').text() + ).toBe(STATUS_UP_LABEL); + expect(component.find('[data-test-subj="xpack.uptime.snapshot.donutChart.up"]').text()).toBe( + `${up}` + ); + expect( + component.find('[data-test-subj="xpack.uptime.snapshot.donutChart.down.label"]').text() + ).toBe(STATUS_DOWN_LABEL); + expect(component.find('[data-test-subj="xpack.uptime.snapshot.donutChart.down"]').text()).toBe( + `${down}` + ); }); }); diff --git a/x-pack/plugins/uptime/public/components/common/charts/donut_chart_legend.tsx b/x-pack/plugins/uptime/public/components/common/charts/donut_chart_legend.tsx index f3b50895fff63..92b9c72e3f1e6 100644 --- a/x-pack/plugins/uptime/public/components/common/charts/donut_chart_legend.tsx +++ b/x-pack/plugins/uptime/public/components/common/charts/donut_chart_legend.tsx @@ -34,14 +34,14 @@ export const DonutChartLegend = ({ down, up }: Props) => { diff --git a/x-pack/plugins/uptime/public/components/common/charts/donut_chart_legend_row.tsx b/x-pack/plugins/uptime/public/components/common/charts/donut_chart_legend_row.tsx index fc67a86db3b48..0f637aff3bfa4 100644 --- a/x-pack/plugins/uptime/public/components/common/charts/donut_chart_legend_row.tsx +++ b/x-pack/plugins/uptime/public/components/common/charts/donut_chart_legend_row.tsx @@ -31,7 +31,7 @@ export const DonutChartLegendRow = ({ color, content, message, 'data-test-subj': - + {message} diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/status_badge.test.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/status_badge.test.tsx index 72ab53cd9667b..c131dff7a698f 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/status_badge.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/status_badge.test.tsx @@ -22,7 +22,7 @@ describe('StatusBadge', () => { it('displays failed message', () => { expect(shallowWithIntl()).toMatchInlineSnapshot(` Failed diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/status_badge.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/status_badge.tsx index f8fbeccaabf42..1d4e0ee723396 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/status_badge.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/status_badge.tsx @@ -19,7 +19,7 @@ export function colorFromStatus(color: UptimeAppColors, status?: string) { case 'succeeded': return color.success; case 'failed': - return color.danger; + return color.dangerBehindText; default: return 'default'; } diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/__tests__/use_monitor_breadcrumbs.test.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/__tests__/use_monitor_breadcrumbs.test.tsx new file mode 100644 index 0000000000000..ec95fcd7f8cfb --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/__tests__/use_monitor_breadcrumbs.test.tsx @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ChromeBreadcrumb } from 'kibana/public'; +import React from 'react'; +import { Route } from 'react-router-dom'; +import { of } from 'rxjs'; +import { MountWithReduxProvider, mountWithRouter } from '../../../../../lib'; +import { KibanaContextProvider } from '../../../../../../../../../src/plugins/kibana_react/public'; +import { useMonitorBreadcrumb } from '../use_monitor_breadcrumb'; +import { OVERVIEW_ROUTE } from '../../../../../../common/constants'; +import { Ping } from '../../../../../../common/runtime_types/ping'; +import { JourneyState } from '../../../../../state/reducers/journey'; + +describe('useMonitorBreadcrumbs', () => { + it('sets the given breadcrumbs', () => { + const [getBreadcrumbs, core] = mockCore(); + + const Component = () => { + useMonitorBreadcrumb({ + activeStep: { monitor: { id: 'test-monitor' } } as Ping, + journey: { details: { timestamp: '2021-01-04T11:25:19.104Z' } } as JourneyState, + }); + return <>Step Water Fall; + }; + + mountWithRouter( + + + + + + + + ); + + expect(getBreadcrumbs()).toMatchInlineSnapshot(` + Array [ + Object { + "href": "/app/uptime", + "onClick": [Function], + "text": "Uptime", + }, + Object { + "href": "/app/uptime/monitor/dGVzdC1tb25pdG9y", + "onClick": [Function], + "text": "test-monitor", + }, + Object { + "text": "Jan 4, 2021 @ 06:25:19.104", + }, + ] + `); + }); +}); + +const mockCore: () => [() => ChromeBreadcrumb[], any] = () => { + let breadcrumbObj: ChromeBreadcrumb[] = []; + const get = () => { + return breadcrumbObj; + }; + const core = { + application: { + getUrlForApp: () => '/app/uptime', + navigateToUrl: jest.fn(), + }, + chrome: { + setBreadcrumbs: (newBreadcrumbs: ChromeBreadcrumb[]) => { + breadcrumbObj = newBreadcrumbs; + }, + }, + uiSettings: { + get: (key: string) => 'MMM D, YYYY @ HH:mm:ss.SSS', + get$: (key: string) => of('MMM D, YYYY @ HH:mm:ss.SSS'), + }, + }; + + return [get, core]; +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail.tsx index fd68edef3226b..d90ff25e71c47 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail.tsx @@ -127,6 +127,7 @@ export const StepDetail: React.FC = ({ onClick={handleNextRun} disabled={!nextCheckGroup} iconType="arrowRight" + iconSide="right" aria-label={NEXT_CHECK_BUTTON_TEXT} > {NEXT_CHECK_BUTTON_TEXT} diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx index 58cf8d6e492da..bdc6dbf3f6de2 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx @@ -9,12 +9,11 @@ import { i18n } from '@kbn/i18n'; import React, { useEffect, useCallback, useMemo } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { useHistory } from 'react-router-dom'; -import moment from 'moment'; -import { useBreadcrumbs } from '../../../../hooks/use_breadcrumbs'; import { getJourneySteps } from '../../../../state/actions/journey'; import { journeySelector } from '../../../../state/selectors'; import { useUiSetting$ } from '../../../../../../../../src/plugins/kibana_react/public'; import { StepDetail } from './step_detail'; +import { useMonitorBreadcrumb } from './use_monitor_breadcrumb'; export const NO_STEP_DATA = i18n.translate('xpack.uptime.synthetics.stepDetail.noData', { defaultMessage: 'No data could be found for this step', @@ -48,12 +47,7 @@ export const StepDetailContainer: React.FC = ({ checkGroup, stepIndex }) }; }, [stepIndex, journey]); - useBreadcrumbs([ - ...(activeStep?.monitor?.name ? [{ text: activeStep?.monitor?.name }] : []), - ...(journey?.details?.timestamp - ? [{ text: moment(journey?.details?.timestamp).format(dateFormat) }] - : []), - ]); + useMonitorBreadcrumb({ journey, activeStep }); const handleNextStep = useCallback(() => { history.push(`/journey/${checkGroup}/step/${stepIndex + 1}`); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumb.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumb.tsx new file mode 100644 index 0000000000000..8e228da615ef9 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumb.tsx @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import moment from 'moment'; +import { useBreadcrumbs } from '../../../../hooks/use_breadcrumbs'; +import { useKibana, useUiSetting$ } from '../../../../../../../../src/plugins/kibana_react/public'; +import { JourneyState } from '../../../../state/reducers/journey'; +import { Ping } from '../../../../../common/runtime_types/ping'; +import { PLUGIN } from '../../../../../common/constants/plugin'; + +interface Props { + journey: JourneyState; + activeStep?: Ping; +} + +export const useMonitorBreadcrumb = ({ journey, activeStep }: Props) => { + const [dateFormat] = useUiSetting$('dateFormat'); + + const kibana = useKibana(); + const appPath = kibana.services.application?.getUrlForApp(PLUGIN.ID) ?? ''; + + useBreadcrumbs([ + ...(activeStep?.monitor + ? [ + { + text: activeStep?.monitor?.name || activeStep?.monitor.id, + href: `${appPath}/monitor/${btoa(activeStep?.monitor.id)}`, + }, + ] + : []), + ...(journey?.details?.timestamp + ? [{ text: moment(journey?.details?.timestamp).format(dateFormat) }] + : []), + ]); +}; diff --git a/x-pack/plugins/uptime/public/hooks/__tests__/use_breadcrumbs.test.tsx b/x-pack/plugins/uptime/public/hooks/__tests__/use_breadcrumbs.test.tsx index 9b9af20285304..5b7032f796fc6 100644 --- a/x-pack/plugins/uptime/public/hooks/__tests__/use_breadcrumbs.test.tsx +++ b/x-pack/plugins/uptime/public/hooks/__tests__/use_breadcrumbs.test.tsx @@ -45,9 +45,7 @@ describe('useBreadcrumbs', () => { const urlParams: UptimeUrlParams = getSupportedUrlParams({}); expect(JSON.stringify(getBreadcrumbs())).toEqual( - JSON.stringify( - [makeBaseBreadcrumb('/app/uptime', jest.fn(), urlParams)].concat(expectedCrumbs) - ) + JSON.stringify([makeBaseBreadcrumb('/app/uptime', urlParams)].concat(expectedCrumbs)) ); }); }); diff --git a/x-pack/plugins/uptime/public/hooks/use_breadcrumbs.ts b/x-pack/plugins/uptime/public/hooks/use_breadcrumbs.ts index d7b111f6fadf3..2c2fc88ffa480 100644 --- a/x-pack/plugins/uptime/public/hooks/use_breadcrumbs.ts +++ b/x-pack/plugins/uptime/public/hooks/use_breadcrumbs.ts @@ -6,7 +6,7 @@ import { ChromeBreadcrumb } from 'kibana/public'; import { i18n } from '@kbn/i18n'; -import { useEffect } from 'react'; +import { MouseEvent, useEffect } from 'react'; import { EuiBreadcrumb } from '@elastic/eui'; import { UptimeUrlParams } from '../lib/helper'; import { stringifyUrlParams } from '../lib/helper/stringify_url_params'; @@ -16,11 +16,26 @@ import { PLUGIN } from '../../common/constants/plugin'; const EMPTY_QUERY = '?'; -export const makeBaseBreadcrumb = ( - href: string, - navigateToHref?: (url: string) => Promise, - params?: UptimeUrlParams -): EuiBreadcrumb => { +function handleBreadcrumbClick( + breadcrumbs: ChromeBreadcrumb[], + navigateToHref?: (url: string) => Promise +) { + return breadcrumbs.map((bc) => ({ + ...bc, + ...(bc.href + ? { + onClick: (event: MouseEvent) => { + if (navigateToHref && bc.href) { + event.preventDefault(); + navigateToHref(bc.href); + } + }, + } + : {}), + })); +} + +export const makeBaseBreadcrumb = (href: string, params?: UptimeUrlParams): EuiBreadcrumb => { if (params) { const crumbParams: Partial = { ...params }; // We don't want to encode this values because they are often set to Date.now(), the relative @@ -36,12 +51,6 @@ export const makeBaseBreadcrumb = ( defaultMessage: 'Uptime', }), href, - onClick: (event) => { - if (href && navigateToHref) { - event.preventDefault(); - navigateToHref(href); - } - }, }; }; @@ -51,9 +60,12 @@ export const useBreadcrumbs = (extraCrumbs: ChromeBreadcrumb[]) => { const setBreadcrumbs = kibana.services.chrome?.setBreadcrumbs; const appPath = kibana.services.application?.getUrlForApp(PLUGIN.ID) ?? ''; const navigate = kibana.services.application?.navigateToUrl; + useEffect(() => { if (setBreadcrumbs) { - setBreadcrumbs([makeBaseBreadcrumb(appPath, navigate, params)].concat(extraCrumbs)); + setBreadcrumbs( + handleBreadcrumbClick([makeBaseBreadcrumb(appPath, params)].concat(extraCrumbs), navigate) + ); } }, [appPath, extraCrumbs, navigate, params, setBreadcrumbs]); }; diff --git a/x-pack/plugins/uptime/public/state/actions/types.ts b/x-pack/plugins/uptime/public/state/actions/types.ts index 8d87ae5d52cb2..322f1eb0136b4 100644 --- a/x-pack/plugins/uptime/public/state/actions/types.ts +++ b/x-pack/plugins/uptime/public/state/actions/types.ts @@ -6,7 +6,8 @@ import { Action } from 'redux-actions'; import { IHttpFetchError } from 'src/core/public'; -import { Alert } from '../../../../triggers_actions_ui/public'; +import { Alert } from '../../../../alerts/common'; +import { UptimeAlertTypeParams } from '../alerts/alerts'; export interface AsyncAction { get: (payload: Payload) => Action; @@ -59,5 +60,5 @@ export interface AlertsResult { page: number; perPage: number; total: number; - data: Alert[]; + data: Array>; } diff --git a/x-pack/plugins/uptime/public/state/alerts/alerts.ts b/x-pack/plugins/uptime/public/state/alerts/alerts.ts index aeb81bb413aa7..43186e0ef5422 100644 --- a/x-pack/plugins/uptime/public/state/alerts/alerts.ts +++ b/x-pack/plugins/uptime/public/state/alerts/alerts.ts @@ -20,10 +20,8 @@ import { fetchMonitorAlertRecords, NewAlertParams, } from '../api/alerts'; -import { - ActionConnector as RawActionConnector, - Alert, -} from '../../../../triggers_actions_ui/public'; +import { ActionConnector as RawActionConnector } from '../../../../triggers_actions_ui/public'; +import { Alert } from '../../../../alerts/common'; import { kibanaService } from '../kibana_service'; import { monitorIdSelector } from '../selectors'; import { AlertsResult, MonitorIdParam } from '../actions/types'; @@ -31,13 +29,22 @@ import { simpleAlertEnabled } from '../../lib/alert_types/alert_messages'; export type ActionConnector = Omit; -export const createAlertAction = createAsyncAction('CREATE ALERT'); +/** + * TODO: Use actual AlertType Params type that's specific to Uptime instead of `any` + */ +export type UptimeAlertTypeParams = Record; + +export const createAlertAction = createAsyncAction< + NewAlertParams, + Alert | null +>('CREATE ALERT'); export const getConnectorsAction = createAsyncAction<{}, ActionConnector[]>('GET CONNECTORS'); export const getMonitorAlertsAction = createAsyncAction<{}, AlertsResult | null>('GET ALERTS'); -export const getAnomalyAlertAction = createAsyncAction( - 'GET EXISTING ALERTS' -); +export const getAnomalyAlertAction = createAsyncAction< + MonitorIdParam, + Alert +>('GET EXISTING ALERTS'); export const deleteAlertAction = createAsyncAction<{ alertId: string }, string | null>( 'DELETE ALERTS' ); @@ -47,9 +54,9 @@ export const deleteAnomalyAlertAction = createAsyncAction<{ alertId: string }, a interface AlertState { connectors: AsyncInitState; - newAlert: AsyncInitState; + newAlert: AsyncInitState>; alerts: AsyncInitState; - anomalyAlert: AsyncInitState; + anomalyAlert: AsyncInitState>; alertDeletion: AsyncInitState; anomalyAlertDeletion: AsyncInitState; } diff --git a/x-pack/plugins/uptime/public/state/api/alerts.ts b/x-pack/plugins/uptime/public/state/api/alerts.ts index da86da12a70ca..9d4dd3a1253c3 100644 --- a/x-pack/plugins/uptime/public/state/api/alerts.ts +++ b/x-pack/plugins/uptime/public/state/api/alerts.ts @@ -9,9 +9,10 @@ import { apiService } from './utils'; import { ActionConnector } from '../alerts/alerts'; import { AlertsResult, MonitorIdParam } from '../actions/types'; -import { Alert, AlertAction } from '../../../../triggers_actions_ui/public'; +import { AlertAction } from '../../../../triggers_actions_ui/public'; import { API_URLS } from '../../../common/constants'; import { MonitorStatusTranslations } from '../../../common/translations'; +import { Alert, AlertTypeParams } from '../../../../alerts/common'; const { MONITOR_STATUS } = ACTION_GROUP_DEFINITIONS; @@ -21,7 +22,7 @@ export const fetchConnectors = async () => { return await apiService.get(API_URLS.ALERT_ACTIONS); }; -export interface NewAlertParams { +export interface NewAlertParams extends AlertTypeParams { monitorId: string; monitorName?: string; defaultActions: ActionConnector[]; @@ -80,7 +81,9 @@ export const fetchMonitorAlertRecords = async (): Promise => { return await apiService.get(API_URLS.ALERTS_FIND, data); }; -export const fetchAlertRecords = async ({ monitorId }: MonitorIdParam): Promise => { +export const fetchAlertRecords = async ({ + monitorId, +}: MonitorIdParam): Promise> => { const data = { page: 1, per_page: 500, @@ -90,7 +93,7 @@ export const fetchAlertRecords = async ({ monitorId }: MonitorIdParam): Promise< sort_order: 'asc', }; const alerts = await apiService.get(API_URLS.ALERTS_FIND, data); - return alerts.data.find((alert: Alert) => alert.params.monitorId === monitorId); + return alerts.data.find((alert: Alert) => alert.params.monitorId === monitorId); }; export const disableAlertById = async ({ alertId }: { alertId: string }) => { diff --git a/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts b/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts index 4f9fefa4188e5..d5fd92476dd16 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts @@ -11,7 +11,13 @@ import { getStatusMessage, getUniqueIdsByLoc, } from '../status_check'; -import { AlertType } from '../../../../../alerts/server'; +import { + AlertType, + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, +} from '../../../../../alerts/server'; import { IRouter } from 'kibana/server'; import { UMServerLibs } from '../../lib'; import { UptimeCorePlugins, UptimeCoreSetup } from '../../adapters'; @@ -868,7 +874,7 @@ describe('status check alert', () => { }); describe('alert factory', () => { - let alert: AlertType; + let alert: AlertType; beforeEach(() => { const { server, libs, plugins } = bootstrapDependencies(); diff --git a/x-pack/plugins/uptime/server/lib/alerts/types.ts b/x-pack/plugins/uptime/server/lib/alerts/types.ts index 0a80b36046860..d143e33fb8e96 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/types.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/types.ts @@ -6,10 +6,17 @@ import { UptimeCorePlugins, UptimeCoreSetup } from '../adapters'; import { UMServerLibs } from '../lib'; -import { AlertType } from '../../../../alerts/server'; +import { AlertType, AlertInstanceState, AlertInstanceContext } from '../../../../alerts/server'; +export type UptimeAlertTypeParam = Record; +export type UptimeAlertTypeState = Record; export type UptimeAlertTypeFactory = ( server: UptimeCoreSetup, libs: UMServerLibs, plugins: UptimeCorePlugins -) => AlertType; +) => AlertType< + UptimeAlertTypeParam, + UptimeAlertTypeState, + AlertInstanceState, + AlertInstanceContext +>; diff --git a/x-pack/plugins/uptime/server/lib/alerts/uptime_alert_wrapper.ts b/x-pack/plugins/uptime/server/lib/alerts/uptime_alert_wrapper.ts index 965287ffbde8e..a4a2f2c64db1b 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/uptime_alert_wrapper.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/uptime_alert_wrapper.ts @@ -5,28 +5,46 @@ */ import { SavedObjectsClientContract } from 'kibana/server'; -import { AlertExecutorOptions, AlertType, AlertTypeState } from '../../../../alerts/server'; +import { + AlertExecutorOptions, + AlertInstanceState, + AlertInstanceContext, +} from '../../../../alerts/server'; import { savedObjectsAdapter } from '../saved_objects'; import { DynamicSettings } from '../../../common/runtime_types'; import { createUptimeESClient, UptimeESClient } from '../lib'; +import { UptimeAlertTypeFactory, UptimeAlertTypeParam, UptimeAlertTypeState } from './types'; -export interface UptimeAlertType extends Omit { +export interface UptimeAlertType + extends Omit, 'executor' | 'producer'> { executor: ({ options, uptimeEsClient, dynamicSettings, }: { - options: AlertExecutorOptions; + options: AlertExecutorOptions< + UptimeAlertTypeParam, + UptimeAlertTypeState, + AlertInstanceState, + AlertInstanceContext + >; uptimeEsClient: UptimeESClient; dynamicSettings: DynamicSettings; savedObjectsClient: SavedObjectsClientContract; - }) => Promise; + }) => Promise; } export const uptimeAlertWrapper = (uptimeAlert: UptimeAlertType) => ({ ...uptimeAlert, producer: 'uptime', - executor: async (options: AlertExecutorOptions) => { + executor: async ( + options: AlertExecutorOptions< + UptimeAlertTypeParam, + UptimeAlertTypeState, + AlertInstanceState, + AlertInstanceContext + > + ) => { const { services: { scopedClusterClient: esClient, savedObjectsClient }, } = options; diff --git a/x-pack/test/accessibility/apps/kibana_overview.ts b/x-pack/test/accessibility/apps/kibana_overview.ts index 3ffcf20c3399b..109562ecb8166 100644 --- a/x-pack/test/accessibility/apps/kibana_overview.ts +++ b/x-pack/test/accessibility/apps/kibana_overview.ts @@ -10,7 +10,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'home']); const a11y = getService('a11y'); - describe('Kibana overview', () => { + // FLAKY: https://github.com/elastic/kibana/issues/82226 + describe.skip('Kibana overview', () => { const esArchiver = getService('esArchiver'); before(async () => { diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts index b4ee273e57d61..30c19f735b75d 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts @@ -13,6 +13,8 @@ import { AlertType, AlertInstanceState, AlertInstanceContext, + AlertTypeState, + AlertTypeParams, } from '../../../../../../../plugins/alerts/server'; export const EscapableStrings = { @@ -50,7 +52,7 @@ function getAlwaysFiringAlertType() { groupsToScheduleActionsInSeries: schema.maybe(schema.arrayOf(schema.nullable(schema.string()))), }); type ParamsType = TypeOf; - interface State { + interface State extends AlertTypeState { groupInSeriesIndex?: number; } interface InstanceState extends AlertInstanceState { @@ -59,7 +61,7 @@ function getAlwaysFiringAlertType() { interface InstanceContext extends AlertInstanceContext { instanceContextValue: boolean; } - const result: AlertType = { + const result: AlertType = { id: 'test.always-firing', name: 'Test: Always Firing', actionGroups: [ @@ -141,7 +143,7 @@ async function alwaysFiringExecutor(alertExecutorOptions: any) { } function getCumulativeFiringAlertType() { - interface State { + interface State extends AlertTypeState { runCount?: number; } interface InstanceState extends AlertInstanceState { @@ -175,7 +177,7 @@ function getCumulativeFiringAlertType() { }; }, }; - return result as AlertType; + return result; } function getNeverFiringAlertType() { @@ -184,7 +186,7 @@ function getNeverFiringAlertType() { reference: schema.string(), }); type ParamsType = TypeOf; - interface State { + interface State extends AlertTypeState { globalStateValue: boolean; } const result: AlertType = { @@ -385,7 +387,7 @@ function getPatternFiringAlertType() { reference: schema.maybe(schema.string()), }); type ParamsType = TypeOf; - interface State { + interface State extends AlertTypeState { patternIndex?: number; } const result: AlertType = { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rbac_legacy.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rbac_legacy.ts index 2b25c82cc92e5..992d9210b9761 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rbac_legacy.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rbac_legacy.ts @@ -62,7 +62,7 @@ export default function alertTests({ getService }: FtrProviderContext) { }); }); - it('should schedule actions on legacy alerts', async () => { + it.skip('should schedule actions on legacy alerts', async () => { const reference = `alert:migrated-to-7.10:${user.username}`; const migratedAlertId = MIGRATED_ALERT_ID[user.username]; diff --git a/x-pack/test/api_integration/apis/search/session.ts b/x-pack/test/api_integration/apis/search/session.ts index 992f25d1f09c7..ee2e4337adc95 100644 --- a/x-pack/test/api_integration/apis/search/session.ts +++ b/x-pack/test/api_integration/apis/search/session.ts @@ -119,8 +119,9 @@ export default function ({ getService }: FtrProviderContext) { const { idMapping } = resp.body.attributes; - expect(Object.values(idMapping)).to.contain(id1); - expect(Object.values(idMapping)).to.contain(id2); + const idMappings = Object.values(idMapping).map((value: any) => value.id); + expect(idMappings).to.contain(id1); + expect(idMappings).to.contain(id2); }); }); }); diff --git a/x-pack/test/api_integration/apis/security/license_downgrade.ts b/x-pack/test/api_integration/apis/security/license_downgrade.ts index 1811f99977b60..a3229bca1f549 100644 --- a/x-pack/test/api_integration/apis/security/license_downgrade.ts +++ b/x-pack/test/api_integration/apis/security/license_downgrade.ts @@ -24,6 +24,7 @@ export default function ({ getService }: FtrProviderContext) { 'minimal_all', 'minimal_read', 'url_create', + 'store_search_session', ]; const trialPrivileges = await supertest .get('/api/security/privileges') diff --git a/x-pack/test/api_integration/apis/security/privileges.ts b/x-pack/test/api_integration/apis/security/privileges.ts index 843dd983adf85..f068dbe7febbd 100644 --- a/x-pack/test/api_integration/apis/security/privileges.ts +++ b/x-pack/test/api_integration/apis/security/privileges.ts @@ -20,9 +20,23 @@ export default function ({ getService }: FtrProviderContext) { // Roles are associated with these privileges, and we shouldn't be removing them in a minor version. const expected = { features: { - discover: ['all', 'read', 'minimal_all', 'minimal_read', 'url_create'], + discover: [ + 'all', + 'read', + 'minimal_all', + 'minimal_read', + 'url_create', + 'store_search_session', + ], visualize: ['all', 'read', 'minimal_all', 'minimal_read', 'url_create'], - dashboard: ['all', 'read', 'minimal_all', 'minimal_read', 'url_create'], + dashboard: [ + 'all', + 'read', + 'minimal_all', + 'minimal_read', + 'url_create', + 'store_search_session', + ], dev_tools: ['all', 'read'], advancedSettings: ['all', 'read'], indexPatterns: ['all', 'read'], diff --git a/x-pack/test/api_integration/apis/security_solution/overview_host.ts b/x-pack/test/api_integration/apis/security_solution/overview_host.ts index f3de9a6481b8f..6077332b3a652 100644 --- a/x-pack/test/api_integration/apis/security_solution/overview_host.ts +++ b/x-pack/test/api_integration/apis/security_solution/overview_host.ts @@ -36,7 +36,7 @@ export default function ({ getService }: FtrProviderContext) { endgameSecurity: 4, filebeatSystemModule: 0, winlogbeatSecurity: 0, - winlogbeatMWSysmonOperational: null, + winlogbeatMWSysmonOperational: 0, }; it('Make sure that we get OverviewHost data', async () => { diff --git a/x-pack/test/apm_api_integration/basic/tests/services/top_services.ts b/x-pack/test/apm_api_integration/basic/tests/services/top_services.ts index 52c9dd74167f5..b160677673331 100644 --- a/x-pack/test/apm_api_integration/basic/tests/services/top_services.ts +++ b/x-pack/test/apm_api_integration/basic/tests/services/top_services.ts @@ -99,21 +99,24 @@ export default function ApiTest({ getService }: FtrProviderContext) { Array [ Object { "avgResponseTime": Object { - "value": 556200.153101878, + "value": 420419.34550767, }, "transactionErrorRate": Object { "value": 0, }, "transactionsPerMinute": Object { - "value": 117.133333333333, + "value": 45.6333333333333, }, }, Object { "avgResponseTime": Object { - "value": 2629229.16666667, + "value": 2382833.33333333, + }, + "transactionErrorRate": Object { + "value": null, }, "transactionsPerMinute": Object { - "value": 3.2, + "value": 0.2, }, }, Object { @@ -151,24 +154,24 @@ export default function ApiTest({ getService }: FtrProviderContext) { }, Object { "avgResponseTime": Object { - "value": 563605.417040359, + "value": 24920.1052631579, }, "transactionErrorRate": Object { "value": 0.0210526315789474, }, "transactionsPerMinute": Object { - "value": 7.43333333333333, + "value": 3.16666666666667, }, }, Object { "avgResponseTime": Object { - "value": 217138.013645224, + "value": 29542.6607142857, }, "transactionErrorRate": Object { - "value": 0.315789473684211, + "value": 0.0357142857142857, }, "transactionsPerMinute": Object { - "value": 17.1, + "value": 1.86666666666667, }, }, Object { @@ -186,6 +189,9 @@ export default function ApiTest({ getService }: FtrProviderContext) { "avgResponseTime": Object { "value": 2319812.5, }, + "transactionErrorRate": Object { + "value": null, + }, "transactionsPerMinute": Object { "value": 0.533333333333333, }, diff --git a/x-pack/test/apm_api_integration/trial/tests/csm/web_core_vitals.ts b/x-pack/test/apm_api_integration/trial/tests/csm/web_core_vitals.ts index 7e970493eb611..50c261d2d37ad 100644 --- a/x-pack/test/apm_api_integration/trial/tests/csm/web_core_vitals.ts +++ b/x-pack/test/apm_api_integration/trial/tests/csm/web_core_vitals.ts @@ -49,7 +49,7 @@ export default function rumServicesApiTests({ getService }: FtrProviderContext) expectSnapshot(response.body).toMatchInline(` Object { - "cls": "0.000", + "cls": 0, "clsRanks": Array [ 100, 0, diff --git a/x-pack/test/detection_engine_api_integration/basic/tests/find_statuses.ts b/x-pack/test/detection_engine_api_integration/basic/tests/find_statuses.ts index fe80402b60731..785b74d334276 100644 --- a/x-pack/test/detection_engine_api_integration/basic/tests/find_statuses.ts +++ b/x-pack/test/detection_engine_api_integration/basic/tests/find_statuses.ts @@ -15,7 +15,7 @@ import { deleteAllRulesStatuses, getSimpleRule, createRule, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, } from '../../utils'; // eslint-disable-next-line import/no-default-export @@ -47,7 +47,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should return a single rule status when a single rule is loaded from a find status with defaults added', async () => { const resBody = await createRule(supertest, getSimpleRule('rule-1', true)); - await waitForRuleSuccess(supertest, resBody.id); + await waitForRuleSuccessOrStatus(supertest, resBody.id); // query the single rule from _find const { body } = await supertest diff --git a/x-pack/test/detection_engine_api_integration/basic/tests/open_close_signals.ts b/x-pack/test/detection_engine_api_integration/basic/tests/open_close_signals.ts index f8a25b0081ef9..2e00be6f77061 100644 --- a/x-pack/test/detection_engine_api_integration/basic/tests/open_close_signals.ts +++ b/x-pack/test/detection_engine_api_integration/basic/tests/open_close_signals.ts @@ -23,7 +23,7 @@ import { createRule, waitForSignalsToBePresent, getSignalsByIds, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, getRuleForSignalTesting, } from '../../utils'; @@ -79,7 +79,7 @@ export default ({ getService }: FtrProviderContext) => { it('should be able to execute and get 10 signals', async () => { const rule = getRuleForSignalTesting(['auditbeat-*']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 10, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); expect(signalsOpen.hits.hits.length).equal(10); @@ -88,7 +88,7 @@ export default ({ getService }: FtrProviderContext) => { it('should be have set the signals in an open state initially', async () => { const rule = getRuleForSignalTesting(['auditbeat-*']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 10, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); const everySignalOpen = signalsOpen.hits.hits.every( @@ -104,7 +104,7 @@ export default ({ getService }: FtrProviderContext) => { it('should be able to get a count of 10 closed signals when closing 10', async () => { const rule = getRuleForSignalTesting(['auditbeat-*']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 10, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); const signalIds = signalsOpen.hits.hits.map((signal) => signal._id); @@ -131,7 +131,7 @@ export default ({ getService }: FtrProviderContext) => { it('should be able close 10 signals immediately and they all should be closed', async () => { const rule = getRuleForSignalTesting(['auditbeat-*']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 10, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); const signalIds = signalsOpen.hits.hits.map((signal) => signal._id); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/add_actions.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/add_actions.ts index bbd85e353e095..a2c3fc6c6c288 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/add_actions.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/add_actions.ts @@ -17,7 +17,7 @@ import { getWebHookAction, getRuleWithWebHookAction, getSimpleRuleOutputWithWebHookAction, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, createRule, } from '../../utils'; @@ -60,7 +60,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const rule = await createRule(supertest, getRuleWithWebHookAction(hookAction.id, true)); - await waitForRuleSuccess(supertest, rule.id); + await waitForRuleSuccessOrStatus(supertest, rule.id); // expected result for status should be 'succeeded' const { body } = await supertest @@ -86,7 +86,7 @@ export default ({ getService }: FtrProviderContext) => { }; const rule = await createRule(supertest, ruleWithAction); - await waitForRuleSuccess(supertest, rule.id); + await waitForRuleSuccessOrStatus(supertest, rule.id); // expected result for status should be 'succeeded' const { body } = await supertest diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_exceptions.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_exceptions.ts index 7e4a6ad86cda5..b90bea66be11f 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_exceptions.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_exceptions.ts @@ -26,7 +26,7 @@ import { removeServerGeneratedProperties, downgradeImmutableRule, createRule, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, installPrePackagedRules, getRule, createExceptionList, @@ -113,7 +113,7 @@ export default ({ getService }: FtrProviderContext) => { }; const rule = await createRule(supertest, ruleWithException); - await waitForRuleSuccess(supertest, rule.id); + await waitForRuleSuccessOrStatus(supertest, rule.id); const bodyToCompare = removeServerGeneratedProperties(rule); const expected: Partial = { @@ -444,7 +444,7 @@ export default ({ getService }: FtrProviderContext) => { ], }; const { id: createdId } = await createRule(supertest, ruleWithException); - await waitForRuleSuccess(supertest, createdId); + await waitForRuleSuccessOrStatus(supertest, createdId); await waitForSignalsToBePresent(supertest, 10, [createdId]); const signalsOpen = await getSignalsByIds(supertest, [createdId]); expect(signalsOpen.hits.hits.length).equal(10); @@ -490,7 +490,7 @@ export default ({ getService }: FtrProviderContext) => { ], }; const rule = await createRule(supertest, ruleWithException); - await waitForRuleSuccess(supertest, rule.id); + await waitForRuleSuccessOrStatus(supertest, rule.id); const signalsOpen = await getSignalsByIds(supertest, [rule.id]); expect(signalsOpen.hits.hits.length).equal(0); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules.ts index 0da12ebba055a..3dbd2acf5b3c2 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules.ts @@ -24,13 +24,16 @@ import { removeServerGeneratedPropertiesIncludingRuleId, getSimpleMlRule, getSimpleMlRuleOutput, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, + waitForSignalsToBePresent, getRuleForSignalTesting, + getRuleForSignalTestingWithTimestampOverride, } from '../../utils'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); describe('create_rules', () => { describe('validation errors', () => { @@ -97,7 +100,7 @@ export default ({ getService }: FtrProviderContext) => { .send(simpleRule) .expect(200); - await waitForRuleSuccess(supertest, body.id); + await waitForRuleSuccessOrStatus(supertest, body.id); const { body: statusBody } = await supertest .post(DETECTION_ENGINE_RULES_STATUS_URL) @@ -201,5 +204,48 @@ export default ({ getService }: FtrProviderContext) => { }); }); }); + + // FAILING ES SNAPSHOT: https://github.com/elastic/kibana/issues/87180 + describe.skip('missing timestamps', () => { + beforeEach(async () => { + await createSignalsIndex(supertest); + // to edit these files run the following script + // cd $HOME/kibana/x-pack && nvm use && node ../scripts/es_archiver edit security_solution/timestamp_override + await esArchiver.load('security_solution/timestamp_override'); + }); + afterEach(async () => { + await deleteSignalsIndex(supertest); + await deleteAllAlerts(supertest); + await esArchiver.unload('security_solution/timestamp_override'); + }); + it('should create a single rule which has a timestamp override and generates two signals with a failing status', async () => { + // should be a failing status because one of the indices in the index pattern is missing + // the timestamp override field. + + // defaults to event.ingested timestamp override. + // event.ingested is one of the timestamp fields set on the es archive data + // inside of x-pack/test/functional/es_archives/security_solution/timestamp_override/data.json.gz + const simpleRule = getRuleForSignalTestingWithTimestampOverride(['myfa*']); + const { body } = await supertest + .post(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send(simpleRule) + .expect(200); + const bodyId = body.id; + + await waitForRuleSuccessOrStatus(supertest, bodyId, 'failed'); + await waitForSignalsToBePresent(supertest, 2, [bodyId]); + + const { body: statusBody } = await supertest + .post(DETECTION_ENGINE_RULES_STATUS_URL) + .set('kbn-xsrf', 'true') + .send({ ids: [bodyId] }) + .expect(200); + + // set to "failed" for now. Will update this with a partial failure + // once I figure out the logic + expect(statusBody[bodyId].current_status.status).to.eql('failed'); + }); + }); }); }; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules_bulk.ts index 7ea47312a5030..2577c6b163604 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules_bulk.ts @@ -22,7 +22,7 @@ import { getSimpleRuleWithoutRuleId, removeServerGeneratedProperties, removeServerGeneratedPropertiesIncludingRuleId, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, } from '../../utils'; // eslint-disable-next-line import/no-default-export @@ -99,7 +99,7 @@ export default ({ getService }: FtrProviderContext): void => { .send([simpleRule]) .expect(200); - await waitForRuleSuccess(supertest, body[0].id); + await waitForRuleSuccessOrStatus(supertest, body[0].id); const { body: statusBody } = await supertest .post(DETECTION_ENGINE_RULES_STATUS_URL) diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts index 21cfab3db6d6a..1f7deddbd5e76 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts @@ -19,7 +19,7 @@ import { deleteSignalsIndex, getSignalsByIds, removeServerGeneratedProperties, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, waitForSignalsToBePresent, } from '../../utils'; @@ -72,7 +72,7 @@ export default ({ getService }: FtrProviderContext) => { supertest, getCreateThreatMatchRulesSchemaMock('rule-1', true) ); - await waitForRuleSuccess(supertest, ruleResponse.id); + await waitForRuleSuccessOrStatus(supertest, ruleResponse.id); const { body: statusBody } = await supertest .post(DETECTION_ENGINE_RULES_STATUS_URL) @@ -128,7 +128,7 @@ export default ({ getService }: FtrProviderContext) => { }; const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 10, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); expect(signalsOpen.hits.hits.length).equal(10); @@ -163,7 +163,7 @@ export default ({ getService }: FtrProviderContext) => { }; const ruleResponse = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, ruleResponse.id); + await waitForRuleSuccessOrStatus(supertest, ruleResponse.id); const signalsOpen = await getSignalsByIds(supertest, [ruleResponse.id]); expect(signalsOpen.hits.hits.length).equal(0); }); @@ -201,7 +201,7 @@ export default ({ getService }: FtrProviderContext) => { }; const ruleResponse = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, ruleResponse.id); + await waitForRuleSuccessOrStatus(supertest, ruleResponse.id); const signalsOpen = await getSignalsByIds(supertest, [ruleResponse.id]); expect(signalsOpen.hits.hits.length).equal(0); }); @@ -239,7 +239,7 @@ export default ({ getService }: FtrProviderContext) => { }; const ruleResponse = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, ruleResponse.id); + await waitForRuleSuccessOrStatus(supertest, ruleResponse.id); const signalsOpen = await getSignalsByIds(supertest, [ruleResponse.id]); expect(signalsOpen.hits.hits.length).equal(0); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/date.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/date.ts index 09cc470defa08..4271ce9b37ebb 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/date.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/date.ts @@ -21,7 +21,7 @@ import { deleteSignalsIndex, getRuleForSignalTesting, getSignalsById, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, waitForSignalsToBePresent, } from '../../../utils'; @@ -50,7 +50,7 @@ export default ({ getService }: FtrProviderContext) => { it('should find all the dates from the data set when no exceptions are set on the rule', async () => { const rule = getRuleForSignalTesting(['date']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); @@ -74,7 +74,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); @@ -105,7 +105,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); @@ -140,7 +140,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); @@ -183,7 +183,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); expect(hits).to.eql([]); @@ -203,7 +203,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); expect(hits).to.eql([]); @@ -221,7 +221,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); @@ -248,7 +248,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); expect(hits).to.eql([]); @@ -268,7 +268,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); @@ -291,7 +291,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); @@ -314,7 +314,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); @@ -338,7 +338,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); expect(hits).to.eql([]); @@ -358,7 +358,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); expect(hits).to.eql([]); @@ -376,7 +376,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); @@ -396,7 +396,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); expect(hits).to.eql([]); @@ -415,7 +415,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); @@ -445,7 +445,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); @@ -477,7 +477,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); @@ -510,7 +510,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); expect(hits).to.eql([]); @@ -534,7 +534,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); @@ -562,7 +562,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); @@ -595,7 +595,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/double.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/double.ts index a5793489cd8d0..158e17299fe9f 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/double.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/double.ts @@ -21,7 +21,7 @@ import { deleteSignalsIndex, getRuleForSignalTesting, getSignalsById, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, waitForSignalsToBePresent, } from '../../../utils'; @@ -52,7 +52,7 @@ export default ({ getService }: FtrProviderContext) => { it('should find all the double from the data set when no exceptions are set on the rule', async () => { const rule = getRuleForSignalTesting(['double']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -71,7 +71,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -98,7 +98,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -133,7 +133,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -176,7 +176,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); expect(hits).to.eql([]); @@ -196,7 +196,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); expect(hits).to.eql([]); @@ -214,7 +214,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -241,7 +241,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); expect(hits).to.eql([]); @@ -261,7 +261,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -280,7 +280,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -299,7 +299,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -318,7 +318,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); expect(hits).to.eql([]); @@ -338,7 +338,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); expect(hits).to.eql([]); @@ -356,7 +356,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -376,7 +376,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); expect(hits).to.eql([]); @@ -395,7 +395,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -421,7 +421,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -444,7 +444,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -467,7 +467,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); expect(hits).to.eql([]); @@ -491,7 +491,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -514,7 +514,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -537,7 +537,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); expect(hits).to.eql([]); @@ -562,7 +562,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -589,7 +589,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -612,7 +612,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -635,7 +635,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -660,7 +660,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -683,7 +683,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -706,7 +706,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -732,7 +732,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/float.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/float.ts index 955d27c086466..0bea2d73151f2 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/float.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/float.ts @@ -21,7 +21,7 @@ import { deleteSignalsIndex, getRuleForSignalTesting, getSignalsById, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, waitForSignalsToBePresent, } from '../../../utils'; @@ -52,7 +52,7 @@ export default ({ getService }: FtrProviderContext) => { it('should find all the float from the data set when no exceptions are set on the rule', async () => { const rule = getRuleForSignalTesting(['float']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -71,7 +71,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -98,7 +98,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -133,7 +133,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -176,7 +176,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); expect(hits).to.eql([]); @@ -196,7 +196,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); expect(hits).to.eql([]); @@ -214,7 +214,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -241,7 +241,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); expect(hits).to.eql([]); @@ -261,7 +261,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -280,7 +280,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -299,7 +299,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -318,7 +318,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); expect(hits).to.eql([]); @@ -338,7 +338,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); expect(hits).to.eql([]); @@ -356,7 +356,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -376,7 +376,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); expect(hits).to.eql([]); @@ -395,7 +395,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -421,7 +421,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -444,7 +444,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -467,7 +467,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); expect(hits).to.eql([]); @@ -491,7 +491,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -514,7 +514,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -537,7 +537,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); expect(hits).to.eql([]); @@ -559,7 +559,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -586,7 +586,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -609,7 +609,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -632,7 +632,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -657,7 +657,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -680,7 +680,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -703,7 +703,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -726,7 +726,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/integer.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/integer.ts index a1275afe288bf..600c1a609a694 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/integer.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/integer.ts @@ -21,7 +21,7 @@ import { deleteSignalsIndex, getRuleForSignalTesting, getSignalsById, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, waitForSignalsToBePresent, } from '../../../utils'; @@ -52,7 +52,7 @@ export default ({ getService }: FtrProviderContext) => { it('should find all the integer from the data set when no exceptions are set on the rule', async () => { const rule = getRuleForSignalTesting(['integer']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -71,7 +71,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -98,7 +98,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -133,7 +133,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -176,7 +176,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); expect(hits).to.eql([]); @@ -196,7 +196,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); expect(hits).to.eql([]); @@ -214,7 +214,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -241,7 +241,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); expect(hits).to.eql([]); @@ -261,7 +261,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -280,7 +280,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -299,7 +299,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -318,7 +318,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); expect(hits).to.eql([]); @@ -338,7 +338,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); expect(hits).to.eql([]); @@ -356,7 +356,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -376,7 +376,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); expect(hits).to.eql([]); @@ -395,7 +395,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -421,7 +421,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -444,7 +444,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -467,7 +467,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); expect(hits).to.eql([]); @@ -491,7 +491,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -514,7 +514,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -537,7 +537,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); expect(hits).to.eql([]); @@ -559,7 +559,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -586,7 +586,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -609,7 +609,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -632,7 +632,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -657,7 +657,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -680,7 +680,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -703,7 +703,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -726,7 +726,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip.ts index 311354c63ca4a..bcdebed3dd45b 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip.ts @@ -21,7 +21,7 @@ import { deleteSignalsIndex, getRuleForSignalTesting, getSignalsById, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, waitForSignalsToBePresent, } from '../../../utils'; @@ -50,7 +50,7 @@ export default ({ getService }: FtrProviderContext) => { it('should find all the ips from the data set when no exceptions are set on the rule', async () => { const rule = getRuleForSignalTesting(['ip']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -69,7 +69,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -96,7 +96,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -131,7 +131,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -174,7 +174,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); expect(ips).to.eql([]); @@ -192,7 +192,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -213,7 +213,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); expect(ips).to.eql([]); @@ -231,7 +231,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -258,7 +258,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); expect(ips).to.eql([]); @@ -278,7 +278,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -297,7 +297,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -316,7 +316,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -335,7 +335,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); expect(ips).to.eql([]); @@ -355,7 +355,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); expect(ips).to.eql([]); @@ -373,7 +373,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -393,7 +393,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); expect(ips).to.eql([]); @@ -412,7 +412,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -437,7 +437,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -460,7 +460,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -488,7 +488,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); expect(ips).to.eql([]); @@ -514,7 +514,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -541,7 +541,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -570,7 +570,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -595,7 +595,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -618,7 +618,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -646,7 +646,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -673,7 +673,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -700,7 +700,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts index 8f4827ec6e71c..9d6f1f2fb297a 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts @@ -21,7 +21,7 @@ import { deleteSignalsIndex, getRuleForSignalTesting, getSignalsById, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, waitForSignalsToBePresent, } from '../../../utils'; @@ -50,7 +50,7 @@ export default ({ getService }: FtrProviderContext) => { it('should find all the ips from the data set when no exceptions are set on the rule', async () => { const rule = getRuleForSignalTesting(['ip_as_array']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -74,7 +74,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -105,7 +105,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -140,7 +140,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -159,7 +159,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -182,7 +182,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -203,7 +203,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); expect(ips).to.eql([]); @@ -221,7 +221,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -246,7 +246,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -273,7 +273,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); expect(ips).to.eql([]); @@ -293,7 +293,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -316,7 +316,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -335,7 +335,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -356,7 +356,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); expect(ips).to.eql([]); @@ -374,7 +374,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -397,7 +397,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); expect(ips).to.eql([[]]); @@ -416,7 +416,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -445,7 +445,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -472,7 +472,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -500,7 +500,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); expect(ips).to.eql([[]]); @@ -536,7 +536,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -567,7 +567,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -592,7 +592,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -615,7 +615,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -646,7 +646,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -687,7 +687,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -721,7 +721,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword.ts index e4e80cb1b65ea..a0183ad794a2f 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword.ts @@ -21,7 +21,7 @@ import { deleteSignalsIndex, getRuleForSignalTesting, getSignalsById, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, waitForSignalsToBePresent, } from '../../../utils'; @@ -50,7 +50,7 @@ export default ({ getService }: FtrProviderContext) => { it('should find all the keyword from the data set when no exceptions are set on the rule', async () => { const rule = getRuleForSignalTesting(['keyword']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -69,7 +69,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -96,7 +96,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -131,7 +131,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -174,7 +174,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); expect(hits).to.eql([]); @@ -194,7 +194,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); expect(hits).to.eql([]); @@ -212,7 +212,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -239,7 +239,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); expect(hits).to.eql([]); @@ -259,7 +259,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -278,7 +278,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -297,7 +297,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -316,7 +316,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); expect(hits).to.eql([]); @@ -336,7 +336,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); expect(hits).to.eql([]); @@ -354,7 +354,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -374,7 +374,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); expect(hits).to.eql([]); @@ -393,7 +393,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -428,7 +428,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -451,7 +451,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -474,7 +474,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -502,7 +502,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); expect(hits).to.eql([]); @@ -526,7 +526,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -549,7 +549,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -577,7 +577,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword_array.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword_array.ts index 01e301c350851..81ea04de5def0 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword_array.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword_array.ts @@ -21,7 +21,7 @@ import { deleteSignalsIndex, getRuleForSignalTesting, getSignalsById, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, waitForSignalsToBePresent, } from '../../../utils'; @@ -50,7 +50,7 @@ export default ({ getService }: FtrProviderContext) => { it('should find all the keyword from the data set when no exceptions are set on the rule', async () => { const rule = getRuleForSignalTesting(['keyword_as_array']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -74,7 +74,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -105,7 +105,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -140,7 +140,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -161,7 +161,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); expect(hits).to.eql([]); @@ -179,7 +179,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -206,7 +206,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); expect(hits).to.eql([]); @@ -226,7 +226,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -249,7 +249,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -268,7 +268,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -289,7 +289,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); expect(hits).to.eql([]); @@ -307,7 +307,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -330,7 +330,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); expect(hits).to.eql([[]]); @@ -349,7 +349,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -388,7 +388,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -426,7 +426,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -453,7 +453,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -480,7 +480,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -508,7 +508,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); expect(hits).to.eql([[]]); @@ -532,7 +532,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -555,7 +555,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -578,7 +578,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -609,7 +609,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/long.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/long.ts index ee52c41bc78e8..56667dbca925e 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/long.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/long.ts @@ -21,7 +21,7 @@ import { deleteSignalsIndex, getRuleForSignalTesting, getSignalsById, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, waitForSignalsToBePresent, } from '../../../utils'; @@ -52,7 +52,7 @@ export default ({ getService }: FtrProviderContext) => { it('should find all the long from the data set when no exceptions are set on the rule', async () => { const rule = getRuleForSignalTesting(['long']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -71,7 +71,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -98,7 +98,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -133,7 +133,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -176,7 +176,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); expect(hits).to.eql([]); @@ -196,7 +196,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); expect(hits).to.eql([]); @@ -214,7 +214,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -241,7 +241,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); expect(hits).to.eql([]); @@ -261,7 +261,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -280,7 +280,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -299,7 +299,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -318,7 +318,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); expect(hits).to.eql([]); @@ -338,7 +338,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); expect(hits).to.eql([]); @@ -356,7 +356,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -376,7 +376,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); expect(hits).to.eql([]); @@ -395,7 +395,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -421,7 +421,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -444,7 +444,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -467,7 +467,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); expect(hits).to.eql([]); @@ -491,7 +491,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -514,7 +514,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -537,7 +537,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); expect(hits).to.eql([]); @@ -559,7 +559,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -586,7 +586,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -609,7 +609,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -632,7 +632,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -657,7 +657,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -680,7 +680,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -703,7 +703,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -726,7 +726,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts index 095d885149389..74507fc030e68 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts @@ -22,7 +22,7 @@ import { deleteSignalsIndex, getRuleForSignalTesting, getSignalsById, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, waitForSignalsToBePresent, } from '../../../utils'; @@ -53,7 +53,7 @@ export default ({ getService }: FtrProviderContext) => { it('should find all the text from the data set when no exceptions are set on the rule', async () => { const rule = getRuleForSignalTesting(['text']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -72,7 +72,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -99,7 +99,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -134,7 +134,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -177,7 +177,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql([]); @@ -195,7 +195,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -214,7 +214,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql([]); @@ -232,7 +232,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -253,7 +253,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql([]); @@ -271,7 +271,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -298,7 +298,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql([]); @@ -316,7 +316,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -335,7 +335,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql(['word four', 'word one', 'word three', 'word two']); @@ -353,7 +353,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -374,7 +374,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -393,7 +393,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -412,7 +412,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -431,7 +431,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql([]); @@ -451,7 +451,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql([]); @@ -469,7 +469,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -489,7 +489,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql([]); @@ -508,7 +508,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -534,7 +534,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -557,7 +557,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -585,7 +585,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql([]); @@ -609,7 +609,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -637,7 +637,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -660,7 +660,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -688,7 +688,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql([]); @@ -714,7 +714,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -737,7 +737,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -765,7 +765,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -790,7 +790,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -818,7 +818,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -841,7 +841,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -869,7 +869,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts index ed63f1a0db25f..9a77cee6be1eb 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts @@ -21,7 +21,7 @@ import { deleteSignalsIndex, getRuleForSignalTesting, getSignalsById, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, waitForSignalsToBePresent, } from '../../../utils'; @@ -50,7 +50,7 @@ export default ({ getService }: FtrProviderContext) => { it('should find all the text from the data set when no exceptions are set on the rule', async () => { const rule = getRuleForSignalTesting(['text_as_array']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -74,7 +74,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -105,7 +105,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -140,7 +140,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -161,7 +161,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql([]); @@ -179,7 +179,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -206,7 +206,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql([]); @@ -226,7 +226,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -249,7 +249,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -268,7 +268,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -289,7 +289,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql([]); @@ -307,7 +307,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -330,7 +330,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql([[]]); @@ -349,7 +349,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -388,7 +388,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -426,7 +426,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -453,7 +453,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -480,7 +480,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -508,7 +508,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql([[]]); @@ -532,7 +532,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -555,7 +555,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -578,7 +578,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -604,7 +604,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/find_statuses.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/find_statuses.ts index 8bb4c45d91bdd..dfec35e4a64f3 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/find_statuses.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/find_statuses.ts @@ -14,7 +14,7 @@ import { deleteSignalsIndex, deleteAllRulesStatuses, getSimpleRule, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, createRule, } from '../../utils'; @@ -66,7 +66,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should return a single rule status when a single rule is loaded from a find status with defaults added', async () => { const resBody = await createRule(supertest, getSimpleRule('rule-1', true)); - await waitForRuleSuccess(supertest, resBody.id); + await waitForRuleSuccessOrStatus(supertest, resBody.id); // query the single rule from _find const { body } = await supertest diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts index 64ee42fdb3f3e..34f7074326550 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts @@ -22,7 +22,7 @@ import { getSignalsByIds, getSignalsByRuleIds, getSimpleRule, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, waitForSignalsToBePresent, } from '../../utils'; import { SIGNALS_TEMPLATE_VERSION } from '../../../../plugins/security_solution/server/lib/detection_engine/routes/index/get_signals_template'; @@ -63,7 +63,7 @@ export default ({ getService }: FtrProviderContext) => { query: `_id:${ID}`, }; const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); expect(signalsOpen.hits.hits.length).greaterThan(0); @@ -75,7 +75,7 @@ export default ({ getService }: FtrProviderContext) => { query: `_id:${ID}`, }; const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); expect(signalsOpen.hits.hits[0]._source.signal.rule.rule_id).eql(getSimpleRule().rule_id); @@ -87,7 +87,7 @@ export default ({ getService }: FtrProviderContext) => { query: `_id:${ID}`, }; const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); // remove rule to cut down on touch points for test changes when the rule format changes @@ -136,7 +136,7 @@ export default ({ getService }: FtrProviderContext) => { query: `_id:${ID}`, }; const { id: createdId } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, createdId); + await waitForRuleSuccessOrStatus(supertest, createdId); await waitForSignalsToBePresent(supertest, 1, [createdId]); // Run signals on top of that 1 signal which should create a single signal (on top of) a signal @@ -146,7 +146,7 @@ export default ({ getService }: FtrProviderContext) => { }; const { id } = await createRule(supertest, ruleForSignals); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); // Get our single signal on top of a signal @@ -201,7 +201,8 @@ export default ({ getService }: FtrProviderContext) => { }); }); - describe('EQL Rules', () => { + // ES PROMOTION FAILURE: http://github.com/elastic/kibana/issues/86709 + describe.skip('EQL Rules', () => { it('generates signals from EQL sequences in the expected form', async () => { const rule: EqlCreateSchema = { ...getRuleForSignalTesting(['auditbeat-*']), @@ -211,7 +212,7 @@ export default ({ getService }: FtrProviderContext) => { query: 'sequence by host.name [any where true] [any where true]', }; const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signals = await getSignalsByRuleIds(supertest, ['eql-rule']); const signal = signals.hits.hits[0]._source.signal; @@ -266,7 +267,7 @@ export default ({ getService }: FtrProviderContext) => { query: 'sequence by host.name [any where true] [any where true]', }; const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsByRuleIds(supertest, ['eql-rule']); const sequenceSignal = signalsOpen.hits.hits.find( @@ -354,7 +355,7 @@ export default ({ getService }: FtrProviderContext) => { }; const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); expect(signalsOpen.hits.hits.length).greaterThan(0); @@ -367,7 +368,7 @@ export default ({ getService }: FtrProviderContext) => { }; const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); expect(signalsOpen.hits.hits[0]._source.signal.rule.rule_id).eql(getSimpleRule().rule_id); @@ -379,7 +380,7 @@ export default ({ getService }: FtrProviderContext) => { query: '_id:1', }; const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); // remove rule to cut down on touch points for test changes when the rule format changes @@ -423,7 +424,7 @@ export default ({ getService }: FtrProviderContext) => { query: '_id:1', }; const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); // Run signals on top of that 1 signal which should create a single signal (on top of) a signal @@ -432,7 +433,7 @@ export default ({ getService }: FtrProviderContext) => { rule_id: 'signal-on-signal', }; const { id: createdId } = await createRule(supertest, ruleForSignals); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [createdId]); // Get our single signal on top of a signal @@ -507,7 +508,7 @@ export default ({ getService }: FtrProviderContext) => { query: '_id:1', }; const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); expect(signalsOpen.hits.hits.length).greaterThan(0); @@ -519,7 +520,7 @@ export default ({ getService }: FtrProviderContext) => { query: '_id:1', }; const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); expect(signalsOpen.hits.hits[0]._source.signal.rule.rule_id).eql(getSimpleRule().rule_id); @@ -531,7 +532,7 @@ export default ({ getService }: FtrProviderContext) => { query: '_id:1', }; const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); // remove rule to cut down on touch points for test changes when the rule format changes @@ -581,7 +582,7 @@ export default ({ getService }: FtrProviderContext) => { query: '_id:1', }; const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); // Run signals on top of that 1 signal which should create a single signal (on top of) a signal @@ -590,7 +591,7 @@ export default ({ getService }: FtrProviderContext) => { rule_id: 'signal-on-signal', }; const { id: createdId } = await createRule(supertest, ruleForSignals); - await waitForRuleSuccess(supertest, createdId); + await waitForRuleSuccessOrStatus(supertest, createdId); await waitForSignalsToBePresent(supertest, 1, [createdId]); // Get our single signal on top of a signal @@ -660,7 +661,7 @@ export default ({ getService }: FtrProviderContext) => { const executeRuleAndGetSignals = async (rule: QueryCreateSchema) => { const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsResponse = await getSignalsByIds(supertest, [id]); const signals = signalsResponse.hits.hits.map((hit) => hit._source); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/open_close_signals.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/open_close_signals.ts index 87e3b145ed6fd..ee787f1b616e3 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/open_close_signals.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/open_close_signals.ts @@ -23,7 +23,7 @@ import { createRule, waitForSignalsToBePresent, getSignalsByIds, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, getRuleForSignalTesting, } from '../../utils'; import { createUserAndRole } from '../roles_users_utils'; @@ -82,7 +82,7 @@ export default ({ getService }: FtrProviderContext) => { it('should be able to execute and get 10 signals', async () => { const rule = getRuleForSignalTesting(['auditbeat-*']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 10, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); expect(signalsOpen.hits.hits.length).equal(10); @@ -91,7 +91,7 @@ export default ({ getService }: FtrProviderContext) => { it('should be have set the signals in an open state initially', async () => { const rule = getRuleForSignalTesting(['auditbeat-*']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 10, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); const everySignalOpen = signalsOpen.hits.hits.every( @@ -107,7 +107,7 @@ export default ({ getService }: FtrProviderContext) => { it('should be able to get a count of 10 closed signals when closing 10', async () => { const rule = getRuleForSignalTesting(['auditbeat-*']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 10, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); const signalIds = signalsOpen.hits.hits.map((signal) => signal._id); @@ -134,7 +134,7 @@ export default ({ getService }: FtrProviderContext) => { it('should be able close signals immediately and they all should be closed', async () => { const rule = getRuleForSignalTesting(['auditbeat-*']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); const signalIds = signalsOpen.hits.hits.map((signal) => signal._id); @@ -169,7 +169,7 @@ export default ({ getService }: FtrProviderContext) => { it('should NOT be able to close signals with t1 analyst user', async () => { const rule = getRuleForSignalTesting(['auditbeat-*']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); await createUserAndRole(securityService, ROLES.t1_analyst); const signalsOpen = await getSignalsByIds(supertest, [id]); @@ -207,7 +207,7 @@ export default ({ getService }: FtrProviderContext) => { it('should be able to close signals with soc_manager user', async () => { const rule = getRuleForSignalTesting(['auditbeat-*']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const userAndRole = ROLES.soc_manager; await createUserAndRole(securityService, userAndRole); diff --git a/x-pack/test/detection_engine_api_integration/utils.ts b/x-pack/test/detection_engine_api_integration/utils.ts index 5a36b950b6a5b..9cff40758bd49 100644 --- a/x-pack/test/detection_engine_api_integration/utils.ts +++ b/x-pack/test/detection_engine_api_integration/utils.ts @@ -119,6 +119,25 @@ export const getRuleForSignalTesting = ( from: '1900-01-01T00:00:00.000Z', }); +export const getRuleForSignalTestingWithTimestampOverride = ( + index: string[], + ruleId = 'rule-1', + enabled = true, + timestampOverride = 'event.ingested' +): QueryCreateSchema => ({ + name: 'Signal Testing Query', + description: 'Tests a simple query', + enabled, + risk_score: 1, + rule_id: ruleId, + severity: 'high', + index, + type: 'query', + query: '*:*', + timestamp_override: timestampOverride, + from: '1900-01-01T00:00:00.000Z', +}); + /** * This is a typical simple rule for testing that is easy for most basic testing * @param ruleId The rule id @@ -864,21 +883,22 @@ export const getRule = async ( }; /** - * Waits for the rule in find status to be succeeded before continuing + * Waits for the rule in find status to be 'succeeded' + * or the provided status, before continuing * @param supertest Deps */ -export const waitForRuleSuccess = async ( +export const waitForRuleSuccessOrStatus = async ( supertest: SuperTest, - id: string + id: string, + status: 'succeeded' | 'failed' | 'partial failure' = 'succeeded' ): Promise => { - // wait for Task Manager to finish executing the rule await waitFor(async () => { const { body } = await supertest .post(`${DETECTION_ENGINE_RULES_URL}/_find_statuses`) .set('kbn-xsrf', 'true') .send({ ids: [id] }) .expect(200); - return body[id]?.current_status?.status === 'succeeded'; + return body[id]?.current_status?.status === status; }, 'waitForRuleSuccess'); }; diff --git a/x-pack/test/fleet_api_integration/apis/agents_setup.ts b/x-pack/test/fleet_api_integration/apis/agents_setup.ts index 85e533a569c87..22149b87b3704 100644 --- a/x-pack/test/fleet_api_integration/apis/agents_setup.ts +++ b/x-pack/test/fleet_api_integration/apis/agents_setup.ts @@ -66,6 +66,8 @@ export default function (providerContext: FtrProviderContext) { '.ds-logs-*', '.ds-metrics-*', '.ds-traces-*', + '.logs-endpoint.diagnostic.collection-*', + '.ds-.logs-endpoint.diagnostic.collection-*', ], privileges: ['write', 'create_index', 'indices:admin/auto_create'], allow_restricted_indices: false, diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts b/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts index 9dcd3ea100589..1d5f864c27eea 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts @@ -433,6 +433,7 @@ const expectAssetsInstalled = ({ ...res.attributes, installed_kibana: sortBy(res.attributes.installed_kibana, (o: AssetReference) => o.type), installed_es: sortBy(res.attributes.installed_es, (o: AssetReference) => o.type), + package_assets: sortBy(res.attributes.package_assets, (o: AssetReference) => o.type), }; expect(sortedRes).eql({ installed_kibana: [ @@ -495,6 +496,29 @@ const expectAssetsInstalled = ({ test_logs: 'logs-all_assets.test_logs-*', test_metrics: 'metrics-all_assets.test_metrics-*', }, + package_assets: [ + { id: '333a22a1-e639-5af5-ae62-907ffc83d603', type: 'epm-packages-assets' }, + { id: '256f3dad-6870-56c3-80a1-8dfa11e2d568', type: 'epm-packages-assets' }, + { id: '3fa0512f-bc01-5c2e-9df1-bc2f2a8259c8', type: 'epm-packages-assets' }, + { id: 'ea334ad8-80c2-5acd-934b-2a377290bf97', type: 'epm-packages-assets' }, + { id: '96c6eb85-fe2e-56c6-84be-5fda976796db', type: 'epm-packages-assets' }, + { id: '2d73a161-fa69-52d0-aa09-1bdc691b95bb', type: 'epm-packages-assets' }, + { id: '0a00c2d2-ce63-5b9c-9aa0-0cf1938f7362', type: 'epm-packages-assets' }, + { id: '691f0505-18c5-57a6-9f40-06e8affbdf7a', type: 'epm-packages-assets' }, + { id: 'b36e6dd0-58f7-5dd0-a286-8187e4019274', type: 'epm-packages-assets' }, + { id: 'f839c76e-d194-555a-90a1-3265a45789e4', type: 'epm-packages-assets' }, + { id: '9af7bbb3-7d8a-50fa-acc9-9dde6f5efca2', type: 'epm-packages-assets' }, + { id: '1e97a20f-9d1c-529b-8ff2-da4e8ba8bb71', type: 'epm-packages-assets' }, + { id: '8cfe0a2b-7016-5522-87e4-6d352360d1fc', type: 'epm-packages-assets' }, + { id: 'bd5ff3c5-655e-5385-9918-b60ff3040aad', type: 'epm-packages-assets' }, + { id: '0954ce3b-3165-5c1f-a4c0-56eb5f2fa487', type: 'epm-packages-assets' }, + { id: '60d6d054-57e4-590f-a580-52bf3f5e7cca', type: 'epm-packages-assets' }, + { id: '47758dc2-979d-5fbe-a2bd-9eded68a5a43', type: 'epm-packages-assets' }, + { id: '318959c9-997b-5a14-b328-9fc7355b4b74', type: 'epm-packages-assets' }, + { id: 'e786cbd9-0f3b-5a0b-82a6-db25145ebf58', type: 'epm-packages-assets' }, + { id: '53c94591-aa33-591d-8200-cd524c2a0561', type: 'epm-packages-assets' }, + { id: 'b658d2d4-752e-54b8-afc2-4c76155c1466', type: 'epm-packages-assets' }, + ], name: 'all_assets', version: '0.1.0', internal: false, diff --git a/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts b/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts index d72209edf6501..7b264f949532e 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts @@ -322,6 +322,26 @@ export default function (providerContext: FtrProviderContext) { test_logs: 'logs-all_assets.test_logs-*', test_metrics: 'metrics-all_assets.test_metrics-*', }, + package_assets: [ + { id: '3eb4c54a-638f-51b6-84e2-d53f5a666e37', type: 'epm-packages-assets' }, + { id: '4acfbf69-7a27-5c58-9c99-7c86843d958f', type: 'epm-packages-assets' }, + { id: '938655df-b339-523c-a9e4-123c89c0e1e1', type: 'epm-packages-assets' }, + { id: 'eec4606c-dbfa-565b-8e9c-fce1e641f3fc', type: 'epm-packages-assets' }, + { id: 'ef67e7e0-dca3-5a62-a42a-745db5ad7c1f', type: 'epm-packages-assets' }, + { id: '64239d25-be40-5e10-94b5-f6b74b8c5474', type: 'epm-packages-assets' }, + { id: '071b5113-4c9f-5ee9-aafe-d098a4c066f6', type: 'epm-packages-assets' }, + { id: '498d8215-2613-5399-9a13-fa4f0bf513e2', type: 'epm-packages-assets' }, + { id: 'd2f87071-c866-503a-8fcb-7b23a8c7afbf', type: 'epm-packages-assets' }, + { id: '5a080eba-f482-545c-8695-6ccbd426b2a2', type: 'epm-packages-assets' }, + { id: '28523a82-1328-578d-84cb-800970560200', type: 'epm-packages-assets' }, + { id: 'cc1e3e1d-f27b-5d05-86f6-6e4b9a47c7dc', type: 'epm-packages-assets' }, + { id: '5c3aa147-089c-5084-beca-53c00e72ac80', type: 'epm-packages-assets' }, + { id: '48e582df-b1d2-5f88-b6ea-ba1fafd3a569', type: 'epm-packages-assets' }, + { id: 'bf3b0b65-9fdc-53c6-a9ca-e76140e56490', type: 'epm-packages-assets' }, + { id: '2e56f08b-1d06-55ed-abee-4708e1ccf0aa', type: 'epm-packages-assets' }, + { id: 'c7bf1a39-e057-58a0-afde-fb4b48751d8c', type: 'epm-packages-assets' }, + { id: '8c665f28-a439-5f43-b5fd-8fda7b576735', type: 'epm-packages-assets' }, + ], name: 'all_assets', version: '0.2.0', internal: false, diff --git a/x-pack/test/functional/apps/lens/chart_data.ts b/x-pack/test/functional/apps/lens/chart_data.ts new file mode 100644 index 0000000000000..62fdbd39912f6 --- /dev/null +++ b/x-pack/test/functional/apps/lens/chart_data.ts @@ -0,0 +1,107 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { DebugState } from '@elastic/charts'; +import expect from '@kbn/expect'; +import { range } from 'lodash'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header']); + const elasticChart = getService('elasticChart'); + + describe('lens chart data', () => { + before(async () => { + await PageObjects.visualize.navigateToNewVisualization(); + await PageObjects.visualize.clickVisType('lens'); + await elasticChart.setNewChartUiDebugFlag(true); + await PageObjects.lens.goToTimeRange(); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension', + operation: 'terms', + field: 'ip', + }); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension', + operation: 'avg', + field: 'bytes', + }); + + await PageObjects.header.waitUntilLoadingHasFinished(); + }); + + const expectedData = [ + { x: '0.53.251.53', y: 4624.75 }, + { x: '0.108.3.2', y: 7359.41 }, + { x: '0.209.80.244', y: 6169.9 }, + { x: '0.228.1.71', y: 7092.8 }, + { x: '0.254.91.215', y: 3835.58 }, + { x: '__other__', y: 5727.24 }, + ]; + + function assertMatchesExpectedData(state: DebugState) { + expect( + state.bars![0].bars.map((bar) => ({ + x: bar.x, + y: Math.round(bar.y * 100) / 100, + })) + ).to.eql(expectedData); + } + + it('should render xy chart', async () => { + const data = await PageObjects.lens.getCurrentChartDebugState(); + assertMatchesExpectedData(data!); + }); + + // Partition chart tests have to be skipped until + // https://github.com/elastic/elastic-charts/issues/917 gets fixed + it.skip('should render pie chart', async () => { + await PageObjects.lens.switchToVisualization('pie'); + await PageObjects.header.waitUntilLoadingHasFinished(); + const data = await PageObjects.lens.getCurrentChartDebugState(); + assertMatchesExpectedData(data!); + }); + + it.skip('should render donut chart', async () => { + await PageObjects.lens.switchToVisualization('donut'); + await PageObjects.header.waitUntilLoadingHasFinished(); + const data = await PageObjects.lens.getCurrentChartDebugState(); + assertMatchesExpectedData(data!); + }); + + it.skip('should render treemap chart', async () => { + await PageObjects.lens.switchToVisualization('treemap'); + await PageObjects.header.waitUntilLoadingHasFinished(); + const data = await PageObjects.lens.getCurrentChartDebugState(); + assertMatchesExpectedData(data!); + }); + + it('should render datatable', async () => { + await PageObjects.lens.switchToVisualization('lnsDatatable'); + await PageObjects.header.waitUntilLoadingHasFinished(); + const terms = await Promise.all( + range(0, 6).map((index) => PageObjects.lens.getDatatableCellText(index, 0)) + ); + const values = await Promise.all( + range(0, 6).map((index) => PageObjects.lens.getDatatableCellText(index, 1)) + ); + expect(terms.map((term) => (term === 'Other' ? '__other__' : term))).to.eql( + expectedData.map(({ x }) => x) + ); + expect(values.map((value) => Math.round(100 * Number(value.replace(',', ''))) / 100)).to.eql( + expectedData.map(({ y }) => y) + ); + }); + + it('should render metric', async () => { + await PageObjects.lens.switchToVisualization('lnsMetric'); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.lens.assertMetric('Average of bytes', '5,727.322'); + }); + }); +} diff --git a/x-pack/test/functional/apps/lens/index.ts b/x-pack/test/functional/apps/lens/index.ts index a885a726ff737..642526d74b687 100644 --- a/x-pack/test/functional/apps/lens/index.ts +++ b/x-pack/test/functional/apps/lens/index.ts @@ -31,6 +31,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./dashboard')); loadTestFile(require.resolve('./persistent_context')); loadTestFile(require.resolve('./colors')); + loadTestFile(require.resolve('./chart_data')); loadTestFile(require.resolve('./drag_and_drop')); loadTestFile(require.resolve('./lens_reporting')); diff --git a/x-pack/test/functional/apps/lens/smokescreen.ts b/x-pack/test/functional/apps/lens/smokescreen.ts index 92ea9508cf837..1d287447461e6 100644 --- a/x-pack/test/functional/apps/lens/smokescreen.ts +++ b/x-pack/test/functional/apps/lens/smokescreen.ts @@ -12,6 +12,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const find = getService('find'); const listingTable = getService('listingTable'); const testSubjects = getService('testSubjects'); + const elasticChart = getService('elasticChart'); describe('lens smokescreen tests', () => { it('should allow creation of lens xy chart', async () => { @@ -191,6 +192,82 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.missingOrFail('lnsXY_yDimensionPanel > lns-dimensionTrigger'); }); + it('should allow creation of a multi-axis chart', async () => { + await PageObjects.visualize.navigateToNewVisualization(); + await PageObjects.visualize.clickVisType('lens'); + await elasticChart.setNewChartUiDebugFlag(true); + await PageObjects.lens.goToTimeRange(); + await PageObjects.lens.switchToVisualization('bar'); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension', + operation: 'terms', + field: 'geo.dest', + }); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension', + operation: 'avg', + field: 'bytes', + }); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension', + operation: 'cardinality', + field: 'bytes', + keepOpen: true, + }); + + await PageObjects.lens.changeAxisSide('right'); + + await PageObjects.lens.closeDimensionEditor(); + + await PageObjects.header.waitUntilLoadingHasFinished(); + + const data = await PageObjects.lens.getCurrentChartDebugState(); + expect(data?.axes?.y.length).to.eql(2); + expect(data?.axes?.y.some(({ position }) => position === 'right')).to.eql(true); + }); + + it('should show value labels on bar charts when enabled', async () => { + // enable value labels + await PageObjects.lens.toggleToolbarPopover('lnsValuesButton'); + await testSubjects.click('lnsXY_valueLabels_inside'); + + await PageObjects.header.waitUntilLoadingHasFinished(); + + // check for value labels + let data = await PageObjects.lens.getCurrentChartDebugState(); + expect(data?.bars?.[0].labels).not.to.eql(0); + + // switch to stacked bar chart + await PageObjects.lens.switchToVisualization('bar_stacked'); + await PageObjects.header.waitUntilLoadingHasFinished(); + + // check for value labels + data = await PageObjects.lens.getCurrentChartDebugState(); + expect(data?.bars?.[0].labels.length).to.eql(0); + }); + + it('should override axis title', async () => { + const axisTitle = 'overridden axis'; + await PageObjects.lens.toggleToolbarPopover('lnsLeftAxisButton'); + await testSubjects.setValue('lnsyLeftAxisTitle', axisTitle, { + clearWithKeyboard: true, + }); + await PageObjects.header.waitUntilLoadingHasFinished(); + + let data = await PageObjects.lens.getCurrentChartDebugState(); + expect(data?.axes?.y?.[0].title).to.eql(axisTitle); + + // hide the gridlines + await testSubjects.click('lnsshowyLeftAxisGridlines'); + await PageObjects.header.waitUntilLoadingHasFinished(); + + data = await PageObjects.lens.getCurrentChartDebugState(); + expect(data?.axes?.y?.[0].gridlines.length).to.eql(0); + }); + it('should transition from a multi-layer stacked bar to donut chart using suggestions', async () => { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickVisType('lens'); @@ -326,6 +403,81 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await PageObjects.lens.getDatatableCellText(0, 1)).to.eql('6,011.351'); }); + it('should create a valid XY chart with references', async () => { + await PageObjects.visualize.navigateToNewVisualization(); + await PageObjects.visualize.clickVisType('lens'); + await PageObjects.lens.goToTimeRange(); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension', + operation: 'date_histogram', + field: '@timestamp', + }); + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension', + operation: 'moving_average', + keepOpen: true, + }); + await PageObjects.lens.configureReference({ + operation: 'sum', + field: 'bytes', + }); + await PageObjects.lens.closeDimensionEditor(); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension', + operation: 'cumulative_sum', + keepOpen: true, + }); + await PageObjects.lens.configureReference({ + field: 'Records', + }); + await PageObjects.lens.closeDimensionEditor(); + + // Two Y axes that are both valid + expect(await find.allByCssSelector('.echLegendItem')).to.have.length(2); + }); + + /** + * The edge cases are: + * + * 1. Showing errors when creating a partial configuration + * 2. Being able to drag in a new field while in partial config + * 3. Being able to switch charts while in partial config + */ + it('should handle edge cases in reference-based operations', async () => { + await PageObjects.visualize.navigateToNewVisualization(); + await PageObjects.visualize.clickVisType('lens'); + await PageObjects.lens.goToTimeRange(); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension', + operation: 'date_histogram', + field: '@timestamp', + }); + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension', + operation: 'cumulative_sum', + }); + expect(await PageObjects.lens.getErrorCount()).to.eql(1); + + await PageObjects.lens.removeDimension('lnsXY_xDimensionPanel'); + expect(await PageObjects.lens.getErrorCount()).to.eql(2); + + await PageObjects.lens.dragFieldToDimensionTrigger( + '@timestamp', + 'lnsXY_xDimensionPanel > lns-empty-dimension' + ); + expect(await PageObjects.lens.getErrorCount()).to.eql(1); + + expect(await PageObjects.lens.hasChartSwitchWarning('lnsDatatable')).to.eql(false); + await PageObjects.lens.switchToVisualization('lnsDatatable'); + + expect(await PageObjects.lens.getDimensionTriggerText('lnsDatatable_metrics')).to.eql( + 'Cumulative sum of (incomplete)' + ); + }); + it('should allow to change index pattern', async () => { await PageObjects.lens.switchFirstLayerIndexPattern('log*'); expect(await PageObjects.lens.getFirstLayerIndexPattern()).to.equal('log*'); diff --git a/x-pack/test/functional/config.js b/x-pack/test/functional/config.js index 814f943a68b05..1815942a06a9a 100644 --- a/x-pack/test/functional/config.js +++ b/x-pack/test/functional/config.js @@ -95,6 +95,7 @@ export default async function ({ readConfigFile }) { defaults: { 'accessibility:disableAnimations': true, 'dateFormat:tz': 'UTC', + 'visualization:visualize:legacyChartsLibrary': true, }, }, // the apps section defines the urls that diff --git a/x-pack/test/functional/es_archives/security_solution/timestamp_override/data.json.gz b/x-pack/test/functional/es_archives/security_solution/timestamp_override/data.json.gz new file mode 100644 index 0000000000000..be351495c2f2e Binary files /dev/null and b/x-pack/test/functional/es_archives/security_solution/timestamp_override/data.json.gz differ diff --git a/x-pack/test/functional/es_archives/security_solution/timestamp_override/mappings.json b/x-pack/test/functional/es_archives/security_solution/timestamp_override/mappings.json new file mode 100644 index 0000000000000..28de7eeb2eb01 --- /dev/null +++ b/x-pack/test/functional/es_archives/security_solution/timestamp_override/mappings.json @@ -0,0 +1,19 @@ +{ + "type": "index", + "value": { + "index": "myfakeindex-1", + "mappings" : { + "properties" : { + "message" : { + "type" : "text", + "fields" : { + "keyword" : { + "type" : "keyword", + "ignore_above" : 256 + } + } + } + } + } + } +} \ No newline at end of file diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts index 2159f939a56f7..13ff6a64f8936 100644 --- a/x-pack/test/functional/page_objects/lens_page.ts +++ b/x-pack/test/functional/page_objects/lens_page.ts @@ -12,6 +12,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont const log = getService('log'); const testSubjects = getService('testSubjects'); const retry = getService('retry'); + const elasticChart = getService('elasticChart'); const find = getService('find'); const comboBox = getService('comboBox'); const browser = getService('browser'); @@ -122,6 +123,32 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont } }, + /** + * Changes the specified dimension to the specified operation and (optinally) field. + * + * @param opts.dimension - the selector of the dimension being changed + * @param opts.operation - the desired operation ID for the dimension + * @param opts.field - the desired field for the dimension + * @param layerIndex - the index of the layer + */ + async configureReference(opts: { + operation?: string; + field?: string; + isPreviousIncompatible?: boolean; + }) { + if (opts.operation) { + const target = await testSubjects.find('indexPattern-subFunction-selection-row'); + await comboBox.openOptionsList(target); + await comboBox.setElement(target, opts.operation); + } + + if (opts.field) { + const target = await testSubjects.find('indexPattern-reference-field-selection-row'); + await comboBox.openOptionsList(target); + await comboBox.setElement(target, opts.field); + } + }, + /** * Drags field to workspace * @@ -190,6 +217,10 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont }); }, + async toggleToolbarPopover(buttonTestSub: string) { + await testSubjects.click(buttonTestSub); + }, + /** * Open the specified dimension. * @@ -327,6 +358,23 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont }); }, + async changeAxisSide(newSide: string) { + await testSubjects.click(`lnsXY_axisSide_groups_${newSide}`); + }, + + /** Counts the visible warnings in the config panel */ + async getErrorCount() { + const moreButton = await testSubjects.exists('configuration-failure-more-errors'); + if (moreButton) { + await retry.try(async () => { + await testSubjects.click('configuration-failure-more-errors'); + await testSubjects.missingOrFail('configuration-failure-more-errors'); + }); + } + const errors = await testSubjects.findAll('configuration-failure-error'); + return errors?.length ?? 0; + }, + /** * Checks a specific subvisualization in the chart switcher for a "data loss" indicator * @@ -445,6 +493,10 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont ); }, + async getCurrentChartDebugState() { + return await elasticChart.getChartDebugData('lnsWorkspace'); + }, + /** * Gets text of the specified datatable header cell * diff --git a/x-pack/test/functional/services/ml/data_visualizer_index_based.ts b/x-pack/test/functional/services/ml/data_visualizer_index_based.ts index 5fc5caf81c23b..fc9dd3d7b033a 100644 --- a/x-pack/test/functional/services/ml/data_visualizer_index_based.ts +++ b/x-pack/test/functional/services/ml/data_visualizer_index_based.ts @@ -33,25 +33,33 @@ export function MachineLearningDataVisualizerIndexBasedProvider({ }, async assertTotalDocCountHeaderExist() { - await testSubjects.existOrFail(`mlDataVisualizerTotalDocCountHeader`); + await retry.tryForTime(5000, async () => { + await testSubjects.existOrFail(`mlDataVisualizerTotalDocCountHeader`); + }); }, async assertTotalDocCountChartExist() { - await testSubjects.existOrFail(`mlFieldDataDocumentCountChart`); + await retry.tryForTime(5000, async () => { + await testSubjects.existOrFail(`mlFieldDataDocumentCountChart`); + }); }, async assertFieldCountPanelExist() { - await testSubjects.existOrFail(`mlDataVisualizerFieldCountPanel`); + await retry.tryForTime(5000, async () => { + await testSubjects.existOrFail(`mlDataVisualizerFieldCountPanel`); + }); }, async assertMetricFieldsSummaryExist() { - await testSubjects.existOrFail(`mlDataVisualizerMetricFieldsSummary`); + await retry.tryForTime(5000, async () => { + await testSubjects.existOrFail(`mlDataVisualizerMetricFieldsSummary`); + }); }, async assertVisibleMetricFieldsCount(count: number) { const expectedCount = count.toString(); - await testSubjects.existOrFail('mlDataVisualizerVisibleMetricFieldsCount'); await retry.tryForTime(5000, async () => { + await testSubjects.existOrFail('mlDataVisualizerVisibleMetricFieldsCount'); const actualCount = await testSubjects.getVisibleText( 'mlDataVisualizerVisibleMetricFieldsCount' ); @@ -64,8 +72,8 @@ export function MachineLearningDataVisualizerIndexBasedProvider({ async assertTotalMetricFieldsCount(count: number) { const expectedCount = count.toString(); - await testSubjects.existOrFail('mlDataVisualizerMetricFieldsCount'); await retry.tryForTime(5000, async () => { + await testSubjects.existOrFail('mlDataVisualizerMetricFieldsCount'); const actualCount = await testSubjects.getVisibleText( 'mlDataVisualizerVisibleMetricFieldsCount' ); @@ -78,8 +86,8 @@ export function MachineLearningDataVisualizerIndexBasedProvider({ async assertVisibleFieldsCount(count: number) { const expectedCount = count.toString(); - await testSubjects.existOrFail('mlDataVisualizerVisibleFieldsCount'); await retry.tryForTime(5000, async () => { + await testSubjects.existOrFail('mlDataVisualizerVisibleFieldsCount'); const actualCount = await testSubjects.getVisibleText('mlDataVisualizerVisibleFieldsCount'); expect(expectedCount).to.eql( expectedCount, @@ -90,8 +98,8 @@ export function MachineLearningDataVisualizerIndexBasedProvider({ async assertTotalFieldsCount(count: number) { const expectedCount = count.toString(); - await testSubjects.existOrFail('mlDataVisualizerTotalFieldsCount'); await retry.tryForTime(5000, async () => { + await testSubjects.existOrFail('mlDataVisualizerTotalFieldsCount'); const actualCount = await testSubjects.getVisibleText('mlDataVisualizerTotalFieldsCount'); expect(expectedCount).to.contain( expectedCount, @@ -101,11 +109,15 @@ export function MachineLearningDataVisualizerIndexBasedProvider({ }, async assertFieldsSummaryExist() { - await testSubjects.existOrFail(`mlDataVisualizerFieldsSummary`); + await retry.tryForTime(5000, async () => { + await testSubjects.existOrFail(`mlDataVisualizerFieldsSummary`); + }); }, async assertDataVisualizerTableExist() { - await testSubjects.existOrFail(`mlDataVisualizerTable`); + await retry.tryForTime(5000, async () => { + await testSubjects.existOrFail(`mlDataVisualizerTable`); + }); }, async assertActionsPanelExists() { diff --git a/x-pack/test/functional_cors/config.ts b/x-pack/test/functional_cors/config.ts index b792aa2d183b6..737cccc5ae33b 100644 --- a/x-pack/test/functional_cors/config.ts +++ b/x-pack/test/functional_cors/config.ts @@ -27,7 +27,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { }; const { protocol, hostname } = kbnTestConfig.getUrlParts(); - const pluginPort = await getPort({ port: 9000 }); + const pluginPort = await getPort(); const originUrl = Url.format({ protocol, hostname, diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alert_create_flyout.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alert_create_flyout.ts index 91eabb6c6472f..bb56b1a7b5269 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alert_create_flyout.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alert_create_flyout.ts @@ -4,13 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import uuid from 'uuid'; import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; - -function generateUniqueKey() { - return uuid.v4().replace(/-/g, ''); -} +import { generateUniqueKey } from '../../lib/get_test_data'; export default ({ getPageObjects, getService }: FtrProviderContext) => { const testSubjects = getService('testSubjects'); diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts index e69fadc67c25f..361e5e632ecc3 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts @@ -4,66 +4,49 @@ * you may not use this file except in compliance with the Elastic License. */ -import uuid from 'uuid'; import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; - -function generateUniqueKey() { - return uuid.v4().replace(/-/g, ''); -} +import { ObjectRemover } from '../../lib/object_remover'; +import { generateUniqueKey, getTestAlertData, getTestActionData } from '../../lib/get_test_data'; export default ({ getPageObjects, getService }: FtrProviderContext) => { - const alerting = getService('alerting'); const testSubjects = getService('testSubjects'); const find = getService('find'); const pageObjects = getPageObjects(['common', 'triggersActionsUI', 'header']); const supertest = getService('supertest'); const retry = getService('retry'); + const objectRemover = new ObjectRemover(supertest); - async function deleteAlerts(alertIds: string[]) { - alertIds.forEach(async (alertId: string) => { - await supertest.delete(`/api/alerts/alert/${alertId}`).set('kbn-xsrf', 'foo').expect(204, ''); - }); - } - - async function createAlert(overwrites: Record = {}) { + async function createAlertManualCleanup(overwrites: Record = {}) { const { body: createdAlert } = await supertest .post(`/api/alerts/alert`) .set('kbn-xsrf', 'foo') - .send({ - enabled: true, - name: generateUniqueKey(), - tags: ['foo', 'bar'], - alertTypeId: 'test.noop', - consumer: 'alerts', - schedule: { interval: '1m' }, - throttle: '1m', - actions: [], - params: {}, - ...overwrites, - }) + .send(getTestAlertData(overwrites)) .expect(200); return createdAlert; } - async function createFailingAlert(overwrites: Record = {}) { - const { body: createdAlert } = await supertest - .post(`/api/alerts/alert`) + async function createFailingAlert() { + return await createAlert({ + alertTypeId: 'test.failing', + schedule: { interval: '30s' }, + }); + } + + async function createAlert(overwrites: Record = {}) { + const createdAlert = await createAlertManualCleanup(overwrites); + objectRemover.add(createdAlert.id, 'alert', 'alerts'); + return createdAlert; + } + + async function createAction(overwrites: Record = {}) { + const { body: createdAction } = await supertest + .post(`/api/actions/action`) .set('kbn-xsrf', 'foo') - .send({ - enabled: true, - name: generateUniqueKey(), - tags: ['foo', 'bar'], - alertTypeId: 'test.failing', - consumer: 'alerts', - schedule: { interval: '30s' }, - throttle: '1m', - actions: [], - params: {}, - ...overwrites, - }) + .send(getTestActionData(overwrites)) .expect(200); - return createdAlert; + objectRemover.add(createdAction.id, 'action', 'actions'); + return createdAction; } async function refreshAlertsList() { @@ -76,11 +59,15 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await testSubjects.click('alertsTab'); }); + afterEach(async () => { + await objectRemover.removeAll(); + }); + it('should display alerts in alphabetical order', async () => { const uniqueKey = generateUniqueKey(); - const a = await createAlert({ name: 'b', tags: [uniqueKey] }); - const b = await createAlert({ name: 'c', tags: [uniqueKey] }); - const c = await createAlert({ name: 'a', tags: [uniqueKey] }); + await createAlert({ name: 'b', tags: [uniqueKey] }); + await createAlert({ name: 'c', tags: [uniqueKey] }); + await createAlert({ name: 'a', tags: [uniqueKey] }); await refreshAlertsList(); await pageObjects.triggersActionsUI.searchAlerts(uniqueKey); @@ -89,8 +76,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { expect(searchResults[0].name).to.eql('a'); expect(searchResults[1].name).to.eql('b'); expect(searchResults[2].name).to.eql('c'); - - await deleteAlerts([a.id, b.id, c.id]); }); it('should search for alert', async () => { @@ -107,7 +92,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { interval: '1m', }, ]); - await deleteAlerts([createdAlert.id]); }); it('should search for tags', async () => { @@ -124,16 +108,14 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { interval: '1m', }, ]); - await deleteAlerts([createdAlert.id]); }); it('should display an empty list when search did not return any alerts', async () => { - const createdAlert = await createAlert(); + await createAlert(); await refreshAlertsList(); await pageObjects.triggersActionsUI.searchAlerts(`An Alert That For Sure Doesn't Exist!`); expect(await pageObjects.triggersActionsUI.isAlertsListDisplayed()).to.eql(true); - await deleteAlerts([createdAlert.id]); }); it('should disable single alert', async () => { @@ -152,7 +134,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const disableSwitchAfterDisable = await testSubjects.find('disableSwitch'); const isChecked = await disableSwitchAfterDisable.getAttribute('aria-checked'); expect(isChecked).to.eql('true'); - await deleteAlerts([createdAlert.id]); }); it('should re-enable single alert', async () => { @@ -177,7 +158,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const disableSwitchAfterReEnable = await testSubjects.find('disableSwitch'); const isChecked = await disableSwitchAfterReEnable.getAttribute('aria-checked'); expect(isChecked).to.eql('false'); - await deleteAlerts([createdAlert.id]); }); it('should mute single alert', async () => { @@ -196,7 +176,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const muteSwitchAfterMute = await testSubjects.find('muteSwitch'); const isChecked = await muteSwitchAfterMute.getAttribute('aria-checked'); expect(isChecked).to.eql('true'); - await deleteAlerts([createdAlert.id]); }); it('should unmute single alert', async () => { @@ -221,12 +200,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const muteSwitchAfterUnmute = await testSubjects.find('muteSwitch'); const isChecked = await muteSwitchAfterUnmute.getAttribute('aria-checked'); expect(isChecked).to.eql('false'); - await deleteAlerts([createdAlert.id]); }); it('should delete single alert', async () => { - const firstAlert = await createAlert(); - const secondAlert = await createAlert(); + await createAlert(); + const secondAlert = await createAlertManualCleanup(); await refreshAlertsList(); await pageObjects.triggersActionsUI.searchAlerts(secondAlert.name); @@ -245,7 +223,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.triggersActionsUI.searchAlerts(secondAlert.name); const searchResultsAfterDelete = await pageObjects.triggersActionsUI.getAlertsList(); expect(searchResultsAfterDelete.length).to.eql(0); - await deleteAlerts([firstAlert.id]); }); it('should mute all selection', async () => { @@ -269,7 +246,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const muteSwitch = await testSubjects.find('muteSwitch'); const isChecked = await muteSwitch.getAttribute('aria-checked'); expect(isChecked).to.eql('true'); - await deleteAlerts([createdAlert.id]); }); it('should unmute all selection', async () => { @@ -295,7 +271,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const muteSwitch = await testSubjects.find('muteSwitch'); const isChecked = await muteSwitch.getAttribute('aria-checked'); expect(isChecked).to.eql('false'); - await deleteAlerts([createdAlert.id]); }); it('should disable all selection', async () => { @@ -319,7 +294,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const disableSwitch = await testSubjects.find('disableSwitch'); const isChecked = await disableSwitch.getAttribute('aria-checked'); expect(isChecked).to.eql('true'); - await deleteAlerts([createdAlert.id]); }); it('should enable all selection', async () => { @@ -345,12 +319,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const disableSwitch = await testSubjects.find('disableSwitch'); const isChecked = await disableSwitch.getAttribute('aria-checked'); expect(isChecked).to.eql('false'); - await deleteAlerts([createdAlert.id]); }); it('should delete all selection', async () => { const namePrefix = generateUniqueKey(); - const createdAlert = await createAlert({ name: `${namePrefix}-1` }); + const createdAlert = await createAlertManualCleanup({ name: `${namePrefix}-1` }); await refreshAlertsList(); await pageObjects.triggersActionsUI.searchAlerts(namePrefix); @@ -374,13 +347,13 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('should filter alerts by the status', async () => { - const createdAlert = await createAlert(); - const failinfAlert = await createFailingAlert(); + await createAlert(); + const failingAlert = await createFailingAlert(); // initialy alert get Pending status, so we need to retry refresh list logic to get the post execution statuses await retry.try(async () => { await refreshAlertsList(); const refreshResults = await pageObjects.triggersActionsUI.getAlertsListWithStatus(); - expect(refreshResults.map((item) => item.status).sort()).to.eql(['Error', 'Ok']); + expect(refreshResults.map((item: any) => item.status).sort()).to.eql(['Error', 'Ok']); }); await testSubjects.click('alertStatusFilterButton'); await testSubjects.click('alertStatuserrorFilerOption'); // select Error status filter @@ -388,7 +361,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const filterErrorOnlyResults = await pageObjects.triggersActionsUI.getAlertsListWithStatus(); expect(filterErrorOnlyResults).to.eql([ { - name: failinfAlert.name, + name: failingAlert.name, tagsText: 'foo, bar', alertType: 'Test: Failing', interval: '30s', @@ -396,8 +369,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }, ]); }); - - await deleteAlerts([createdAlert.id, failinfAlert.id]); }); it('should display total alerts by status and error banner only when exists alerts with status error', async () => { @@ -421,7 +392,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { ); expect(alertsErrorBannerWhenNoErrors).to.have.length(0); - const failingAlert = await createFailingAlert(); + await createFailingAlert(); await retry.try(async () => { await refreshAlertsList(); const alertsErrorBannerExistErrors = await find.allByCssSelector( @@ -443,13 +414,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { expect(await testSubjects.getVisibleText('totalErrorAlertsCount')).to.be('Error: 1'); expect(await testSubjects.getVisibleText('totalPendingAlertsCount')).to.be('Pending: 0'); expect(await testSubjects.getVisibleText('totalUnknownAlertsCount')).to.be('Unknown: 0'); - - await deleteAlerts([createdAlert.id, failingAlert.id]); }); it('should filter alerts by the alert type', async () => { - const noopAlert = await createAlert(); - const failinfAlert = await createFailingAlert(); + await createAlert(); + const failingAlert = await createFailingAlert(); await refreshAlertsList(); await testSubjects.click('alertTypeFilterButton'); expect(await (await testSubjects.find('alertType0Group')).getVisibleText()).to.eql('Alerts'); @@ -459,27 +428,18 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const filterFailingAlertOnlyResults = await pageObjects.triggersActionsUI.getAlertsList(); expect(filterFailingAlertOnlyResults).to.eql([ { - name: failinfAlert.name, + name: failingAlert.name, tagsText: 'foo, bar', alertType: 'Test: Failing', interval: '30s', }, ]); }); - - await deleteAlerts([noopAlert.id, failinfAlert.id]); }); it('should filter alerts by the action type', async () => { - const noopAlert = await createAlert(); - const action = await alerting.actions.createAction({ - name: `slack-${Date.now()}`, - actionTypeId: '.slack', - config: {}, - secrets: { - webhookUrl: 'https://test', - }, - }); + await createAlert(); + const action = await createAction(); const noopAlertWithAction = await createAlert({ actions: [ { @@ -505,8 +465,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }, ]); }); - - await deleteAlerts([noopAlertWithAction.id, noopAlert.id]); }); }); }; diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors.ts index f55114cf11d14..c28a5ed3794af 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors.ts @@ -4,35 +4,35 @@ * you may not use this file except in compliance with the Elastic License. */ -import uuid from 'uuid'; import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; - -function generateUniqueKey() { - return uuid.v4().replace(/-/g, ''); -} +import { ObjectRemover } from '../../lib/object_remover'; +import { generateUniqueKey, getTestActionData } from '../../lib/get_test_data'; export default ({ getPageObjects, getService }: FtrProviderContext) => { - const alerting = getService('alerting'); const testSubjects = getService('testSubjects'); const pageObjects = getPageObjects(['common', 'triggersActionsUI', 'header']); const find = getService('find'); const retry = getService('retry'); const comboBox = getService('comboBox'); + const supertest = getService('supertest'); describe('Connectors', function () { - before(async () => { - await alerting.actions.createAction({ - name: `slack-${Date.now()}`, - actionTypeId: '.slack', - config: {}, - secrets: { - webhookUrl: 'https://test', - }, - }); + const objectRemover = new ObjectRemover(supertest); + before(async () => { + const { body: createdAction } = await supertest + .post(`/api/actions/action`) + .set('kbn-xsrf', 'foo') + .send(getTestActionData()) + .expect(200); await pageObjects.common.navigateToApp('triggersActions'); await testSubjects.click('connectorsTab'); + objectRemover.add(createdAction.id, 'action', 'actions'); + }); + + after(async () => { + await objectRemover.removeAll(); }); it('should create a connector', async () => { diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts index c9eebe0172874..c9fb82e1ccf8c 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts @@ -9,53 +9,103 @@ import uuid from 'uuid'; import { omit, mapValues, range, flatten } from 'lodash'; import moment from 'moment'; import { FtrProviderContext } from '../../ftr_provider_context'; +import { ObjectRemover } from '../../lib/object_remover'; import { alwaysFiringAlertType } from '../../fixtures/plugins/alerts/server/plugin'; +import { getTestAlertData, getTestActionData } from '../../lib/get_test_data'; export default ({ getPageObjects, getService }: FtrProviderContext) => { const testSubjects = getService('testSubjects'); const pageObjects = getPageObjects(['common', 'triggersActionsUI', 'header', 'alertDetailsUI']); const browser = getService('browser'); const log = getService('log'); - const alerting = getService('alerting'); const retry = getService('retry'); const find = getService('find'); + const supertest = getService('supertest'); + const objectRemover = new ObjectRemover(supertest); + + async function createAction(overwrites: Record = {}) { + const { body: createdAction } = await supertest + .post(`/api/actions/action`) + .set('kbn-xsrf', 'foo') + .send(getTestActionData(overwrites)) + .expect(200); + objectRemover.add(createdAction.id, 'action', 'actions'); + return createdAction; + } + + async function createAlert(overwrites: Record = {}) { + const { body: createdAlert } = await supertest + .post(`/api/alerts/alert`) + .set('kbn-xsrf', 'foo') + .send(getTestAlertData(overwrites)) + .expect(200); + objectRemover.add(createdAlert.id, 'alert', 'alerts'); + return createdAlert; + } + + async function createAlwaysFiringAlert(overwrites: Record = {}) { + const { body: createdAlert } = await supertest + .post(`/api/alerts/alert`) + .set('kbn-xsrf', 'foo') + .send( + getTestAlertData({ + alertTypeId: 'test.always-firing', + ...overwrites, + }) + ) + .expect(200); + objectRemover.add(createdAlert.id, 'alert', 'alerts'); + return createdAlert; + } + + async function createActions(testRunUuid: string) { + return await Promise.all([ + createAction({ name: `slack-${testRunUuid}-${0}` }), + createAction({ name: `slack-${testRunUuid}-${1}` }), + ]); + } + + async function createAlertWithActionsAndParams( + testRunUuid: string, + params: Record = {} + ) { + const actions = await createActions(testRunUuid); + return await createAlwaysFiringAlert({ + name: `test-alert-${testRunUuid}`, + actions: actions.map((action) => ({ + id: action.id, + group: 'default', + params: { + message: 'from alert 1s', + level: 'warn', + }, + })), + params, + }); + } + + async function getAlertInstanceSummary(alertId: string) { + const { body: summary } = await supertest + .get(`/api/alerts/alert/${alertId}/_instance_summary`) + .expect(200); + return summary; + } + + async function muteAlertInstance(alertId: string, alertInstanceId: string) { + const { body: response } = await supertest + .post(`/api/alerts/alert/${alertId}/alert_instance/${alertInstanceId}/_mute`) + .set('kbn-xsrf', 'foo') + .expect(204); + + return response; + } describe('Alert Details', function () { describe('Header', function () { const testRunUuid = uuid.v4(); before(async () => { await pageObjects.common.navigateToApp('triggersActions'); - - const actions = await Promise.all([ - alerting.actions.createAction({ - name: `slack-${testRunUuid}-${0}`, - actionTypeId: '.slack', - config: {}, - secrets: { - webhookUrl: 'https://test', - }, - }), - alerting.actions.createAction({ - name: `slack-${testRunUuid}-${1}`, - actionTypeId: '.slack', - config: {}, - secrets: { - webhookUrl: 'https://test', - }, - }), - ]); - - const alert = await alerting.alerts.createAlwaysFiringWithActions( - `test-alert-${testRunUuid}`, - actions.map((action) => ({ - id: action.id, - group: 'default', - params: { - message: 'from alert 1s', - level: 'warn', - }, - })) - ); + const alert = await createAlertWithActionsAndParams(testRunUuid); // refresh to see alert await browser.refresh(); @@ -69,6 +119,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.triggersActionsUI.clickOnAlertInAlertsList(alert.name); }); + after(async () => { + await objectRemover.removeAll(); + }); + it('renders the alert details', async () => { const headingText = await pageObjects.alertDetailsUI.getHeadingText(); expect(headingText).to.be(`test-alert-${testRunUuid}`); @@ -154,33 +208,41 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); describe('Edit alert button', function () { - const testRunUuid = uuid.v4(); + const alertName = uuid.v4(); + const updatedAlertName = `Changed Alert Name ${alertName}`; - it('should open edit alert flyout', async () => { - await pageObjects.common.navigateToApp('triggersActions'); - const params = { - aggType: 'count', - termSize: 5, - thresholdComparator: '>', - timeWindowSize: 5, - timeWindowUnit: 'm', - groupBy: 'all', - threshold: [1000, 5000], - index: ['.kibana_1'], - timeField: 'alert', - }; - const alert = await alerting.alerts.createAlertWithActions( - testRunUuid, - '.index-threshold', - params, - [ + before(async () => { + await createAlwaysFiringAlert({ + name: alertName, + alertTypeId: '.index-threshold', + params: { + aggType: 'count', + termSize: 5, + thresholdComparator: '>', + timeWindowSize: 5, + timeWindowUnit: 'm', + groupBy: 'all', + threshold: [1000, 5000], + index: ['.kibana_1'], + timeField: 'alert', + }, + actions: [ { group: 'threshold met', id: 'my-server-log', params: { level: 'info', message: ' {{context.message}}' }, }, - ] - ); + ], + }); + }); + + after(async () => { + await objectRemover.removeAll(); + }); + + it('should open edit alert flyout', async () => { + await pageObjects.common.navigateToApp('triggersActions'); + // refresh to see alert await browser.refresh(); @@ -190,13 +252,12 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await testSubjects.existOrFail('alertsList'); // click on first alert - await pageObjects.triggersActionsUI.clickOnAlertInAlertsList(alert.name); + await pageObjects.triggersActionsUI.clickOnAlertInAlertsList(alertName); const editButton = await testSubjects.find('openEditAlertFlyoutButton'); await editButton.click(); expect(await testSubjects.exists('hasActionsDisabled')).to.eql(false); - const updatedAlertName = `Changed Alert Name ${uuid.v4()}`; await testSubjects.setValue('alertNameInput', updatedAlertName, { clearWithKeyboard: true, }); @@ -213,22 +274,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { it('should reset alert when canceling an edit', async () => { await pageObjects.common.navigateToApp('triggersActions'); - const params = { - aggType: 'count', - termSize: 5, - thresholdComparator: '>', - timeWindowSize: 5, - timeWindowUnit: 'm', - groupBy: 'all', - threshold: [1000, 5000], - index: ['.kibana_1'], - timeField: 'alert', - }; - const alert = await alerting.alerts.createAlertWithActions( - testRunUuid, - '.index-threshold', - params - ); + // refresh to see alert await browser.refresh(); @@ -238,13 +284,12 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await testSubjects.existOrFail('alertsList'); // click on first alert - await pageObjects.triggersActionsUI.clickOnAlertInAlertsList(alert.name); + await pageObjects.triggersActionsUI.clickOnAlertInAlertsList(updatedAlertName); const editButton = await testSubjects.find('openEditAlertFlyoutButton'); await editButton.click(); - const updatedAlertName = `Changed Alert Name ${uuid.v4()}`; - await testSubjects.setValue('alertNameInput', updatedAlertName, { + await testSubjects.setValue('alertNameInput', uuid.v4(), { clearWithKeyboard: true, }); @@ -255,24 +300,30 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const nameInputAfterCancel = await testSubjects.find('alertNameInput'); const textAfterCancel = await nameInputAfterCancel.getAttribute('value'); - expect(textAfterCancel).to.eql(alert.name); + expect(textAfterCancel).to.eql(updatedAlertName); await alerting.alerts.deleteAlert(alert.id); }); }); describe('View In App', function () { - const testRunUuid = uuid.v4(); + const alertName = uuid.v4(); beforeEach(async () => { await pageObjects.common.navigateToApp('triggersActions'); }); + after(async () => { + await objectRemover.removeAll(); + }); + it('renders the alert details view in app button', async () => { - const alert = await alerting.alerts.createNoOp(`test-alert-${testRunUuid}`); + const alert = await createAlert({ + name: alertName, + consumer: 'alerting_fixture', + }); // refresh to see alert await browser.refresh(); - await pageObjects.header.waitUntilLoadingHasFinished(); // Verify content @@ -290,14 +341,12 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('renders a disabled alert details view in app button', async () => { - const alert = await alerting.alerts.createAlwaysFiringWithActions( - `test-alert-disabled-nav`, - [] - ); + const alert = await createAlwaysFiringAlert({ + name: `test-alert-disabled-nav`, + }); // refresh to see alert await browser.refresh(); - await pageObjects.header.waitUntilLoadingHasFinished(); // Verify content @@ -318,44 +367,13 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { before(async () => { await pageObjects.common.navigateToApp('triggersActions'); - const actions = await Promise.all([ - alerting.actions.createAction({ - name: `slack-${testRunUuid}-${0}`, - actionTypeId: '.slack', - config: {}, - secrets: { - webhookUrl: 'https://test', - }, - }), - alerting.actions.createAction({ - name: `slack-${testRunUuid}-${1}`, - actionTypeId: '.slack', - config: {}, - secrets: { - webhookUrl: 'https://test', - }, - }), - ]); - const instances = [{ id: 'us-central' }, { id: 'us-east' }, { id: 'us-west' }]; - alert = await alerting.alerts.createAlwaysFiringWithActions( - `test-alert-${testRunUuid}`, - actions.map((action) => ({ - id: action.id, - group: 'default', - params: { - message: 'from alert 1s', - level: 'warn', - }, - })), - { - instances, - } - ); + alert = await createAlertWithActionsAndParams(testRunUuid, { + instances, + }); // refresh to see alert await browser.refresh(); - await pageObjects.header.waitUntilLoadingHasFinished(); // Verify content @@ -366,14 +384,14 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { // await first run to complete so we have an initial state await retry.try(async () => { - const { instances: alertInstances } = await alerting.alerts.getAlertInstanceSummary( - alert.id - ); + const { instances: alertInstances } = await getAlertInstanceSummary(alert.id); expect(Object.keys(alertInstances).length).to.eql(instances.length); }); }); - after(async () => await alerting.alerts.deleteAlert(alert.id)); + after(async () => { + await objectRemover.removeAll(); + }); it('renders the active alert instances', async () => { // refresh to ensure Api call and UI are looking at freshest output @@ -390,8 +408,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { (actionGroup: { id: string; name: string }) => actionGroup.id === actionGroupId )?.name; - const summary = await alerting.alerts.getAlertInstanceSummary(alert.id); - const dateOnAllInstancesFromApiResponse = mapValues( + const summary = await getAlertInstanceSummary(alert.id); + const dateOnAllInstancesFromApiResponse: Record = mapValues( summary.instances, (instance) => instance.activeStartDate ); @@ -410,7 +428,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { .join(', ')}` ); - const instancesList = await pageObjects.alertDetailsUI.getAlertInstancesList(); + const instancesList: any[] = await pageObjects.alertDetailsUI.getAlertInstancesList(); expect(instancesList.map((instance) => omit(instance, 'duration'))).to.eql([ { instance: 'us-central', @@ -480,12 +498,12 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { it('renders the muted inactive alert instances', async () => { // mute an alert instance that doesn't exist - await alerting.alerts.muteAlertInstance(alert.id, 'eu-east'); + await muteAlertInstance(alert.id, 'eu-east'); // refresh to see alert await browser.refresh(); - const instancesList = await pageObjects.alertDetailsUI.getAlertInstancesList(); + const instancesList: any[] = await pageObjects.alertDetailsUI.getAlertInstancesList(); expect( instancesList.filter((alertInstance) => alertInstance.instance === 'eu-east') ).to.eql([ @@ -551,25 +569,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { before(async () => { await pageObjects.common.navigateToApp('triggersActions'); - const actions = await Promise.all([ - alerting.actions.createAction({ - name: `slack-${testRunUuid}-${0}`, - actionTypeId: '.slack', - config: {}, - secrets: { - webhookUrl: 'https://test', - }, - }), - alerting.actions.createAction({ - name: `slack-${testRunUuid}-${1}`, - actionTypeId: '.slack', - config: {}, - secrets: { - webhookUrl: 'https://test', - }, - }), - ]); - const instances = flatten( range(10).map((index) => [ { id: `us-central-${index}` }, @@ -577,26 +576,13 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { { id: `us-west-${index}` }, ]) ); - alert = await alerting.alerts.createAlwaysFiringWithActions( - `test-alert-${testRunUuid}`, - actions.map((action) => ({ - id: action.id, - group: 'default', - params: { - message: 'from alert 1s', - level: 'warn', - }, - })), - { - instances, - } - ); + alert = await createAlertWithActionsAndParams(testRunUuid, { + instances, + }); // await first run to complete so we have an initial state await retry.try(async () => { - const { instances: alertInstances } = await alerting.alerts.getAlertInstanceSummary( - alert.id - ); + const { instances: alertInstances } = await getAlertInstanceSummary(alert.id); expect(Object.keys(alertInstances).length).to.eql(instances.length); }); @@ -614,14 +600,16 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { after(async () => await alerting.alerts.deleteAlert(alert.id)); + after(async () => { + await objectRemover.removeAll(); + }); + const PAGE_SIZE = 10; it('renders the first page', async () => { // Verify content await testSubjects.existOrFail('alertInstancesList'); - const { instances: alertInstances } = await alerting.alerts.getAlertInstanceSummary( - alert.id - ); + const { instances: alertInstances } = await getAlertInstanceSummary(alert.id); const items = await pageObjects.alertDetailsUI.getAlertInstancesList(); expect(items.length).to.eql(PAGE_SIZE); @@ -634,9 +622,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { // Verify content await testSubjects.existOrFail('alertInstancesList'); - const { instances: alertInstances } = await alerting.alerts.getAlertInstanceSummary( - alert.id - ); + const { instances: alertInstances } = await getAlertInstanceSummary(alert.id); await pageObjects.alertDetailsUI.clickPaginationNextPage(); diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/home_page.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/home_page.ts index bd799947256d6..029209ec2024d 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/home_page.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/home_page.ts @@ -6,19 +6,26 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; +import { ObjectRemover } from '../../lib/object_remover'; +import { getTestAlertData, getTestActionData } from '../../lib/get_test_data'; export default ({ getPageObjects, getService }: FtrProviderContext) => { const testSubjects = getService('testSubjects'); const pageObjects = getPageObjects(['common', 'triggersActionsUI', 'header']); const log = getService('log'); const browser = getService('browser'); - const alerting = getService('alerting'); + const supertest = getService('supertest'); + const objectRemover = new ObjectRemover(supertest); describe('Home page', function () { before(async () => { await pageObjects.common.navigateToApp('triggersActions'); }); + after(async () => { + await objectRemover.removeAll(); + }); + it('Loads the app', async () => { await log.debug('Checking for section heading to say Triggers and Actions.'); @@ -58,26 +65,19 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('navigates to an alert details page', async () => { - const action = await alerting.actions.createAction({ - name: `Slack-${Date.now()}`, - actionTypeId: '.slack', - config: {}, - secrets: { - webhookUrl: 'https://test', - }, - }); - - const alert = await alerting.alerts.createAlwaysFiringWithAction( - `test-alert-${Date.now()}`, - { - id: action.id, - group: 'default', - params: { - message: 'from alert 1s', - level: 'warn', - }, - } - ); + const { body: createdAction } = await supertest + .post(`/api/actions/action`) + .set('kbn-xsrf', 'foo') + .send(getTestActionData()) + .expect(200); + objectRemover.add(createdAction.id, 'action', 'actions'); + + const { body: createdAlert } = await supertest + .post(`/api/alerts/alert`) + .set('kbn-xsrf', 'foo') + .send(getTestAlertData()) + .expect(200); + objectRemover.add(createdAlert.id, 'alert', 'alerts'); // refresh to see alert await browser.refresh(); @@ -88,12 +88,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await testSubjects.existOrFail('alertsList'); // click on first alert - await pageObjects.triggersActionsUI.clickOnAlertInAlertsList(alert.name); + await pageObjects.triggersActionsUI.clickOnAlertInAlertsList(createdAlert.name); // Verify url - expect(await browser.getCurrentUrl()).to.contain(`/alert/${alert.id}`); - - await alerting.alerts.deleteAlert(alert.id); + expect(await browser.getCurrentUrl()).to.contain(`/alert/${createdAlert.id}`); }); }); }); diff --git a/x-pack/test/functional_with_es_ssl/config.ts b/x-pack/test/functional_with_es_ssl/config.ts index eedc39b09a8e4..08a44b5040a4f 100644 --- a/x-pack/test/functional_with_es_ssl/config.ts +++ b/x-pack/test/functional_with_es_ssl/config.ts @@ -7,7 +7,6 @@ import { resolve, join } from 'path'; import { CA_CERT_PATH } from '@kbn/dev-utils'; import { FtrConfigProviderContext } from '@kbn/test/types/ftr'; -import { services } from './services'; import { pageObjects } from './page_objects'; // .server-log is specifically not enabled @@ -39,7 +38,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { const returnedObject = { ...xpackFunctionalConfig.getAll(), servers, - services, pageObjects, // list paths to the files that contain your plugins tests testFiles: [ diff --git a/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/server/plugin.ts b/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/server/plugin.ts index f6cbc52e7a421..cf09286fe1ba6 100644 --- a/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/server/plugin.ts +++ b/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/server/plugin.ts @@ -27,7 +27,14 @@ export const noopAlertType: AlertType = { producer: 'alerts', }; -export const alwaysFiringAlertType: AlertType = { +export const alwaysFiringAlertType: AlertType< + { instances: Array<{ id: string; state: any }> }, + { + globalStateValue: boolean; + groupInSeriesIndex: number; + }, + { instanceStateValue: boolean; globalStateValue: boolean; groupInSeriesIndex: number } +> = { id: 'test.always-firing', name: 'Always Firing', actionGroups: [ @@ -37,7 +44,7 @@ export const alwaysFiringAlertType: AlertType = { defaultActionGroupId: 'default', producer: 'alerts', minimumLicenseRequired: 'basic', - async executor(alertExecutorOptions: any) { + async executor(alertExecutorOptions) { const { services, state, params } = alertExecutorOptions; (params.instances || []).forEach((instance: { id: string; state: any }) => { diff --git a/x-pack/test/functional_with_es_ssl/ftr_provider_context.d.ts b/x-pack/test/functional_with_es_ssl/ftr_provider_context.d.ts index bb257cdcbfe1b..ca685f9495bb0 100644 --- a/x-pack/test/functional_with_es_ssl/ftr_provider_context.d.ts +++ b/x-pack/test/functional_with_es_ssl/ftr_provider_context.d.ts @@ -5,8 +5,7 @@ */ import { GenericFtrProviderContext } from '@kbn/test/types/ftr'; - +import { services } from '../functional/services'; import { pageObjects } from './page_objects'; -import { services } from './services'; export type FtrProviderContext = GenericFtrProviderContext; diff --git a/x-pack/test/functional_with_es_ssl/lib/get_test_data.ts b/x-pack/test/functional_with_es_ssl/lib/get_test_data.ts new file mode 100644 index 0000000000000..79868fef53c4b --- /dev/null +++ b/x-pack/test/functional_with_es_ssl/lib/get_test_data.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import uuid from 'uuid'; + +export function generateUniqueKey() { + return uuid.v4().replace(/-/g, ''); +} + +export function getTestAlertData(overwrites = {}) { + return { + enabled: true, + name: generateUniqueKey(), + tags: ['foo', 'bar'], + alertTypeId: 'test.noop', + consumer: 'alerts', + schedule: { interval: '1m' }, + throttle: '1m', + notifyWhen: 'onThrottleInterval', + actions: [], + params: {}, + ...overwrites, + }; +} + +export function getTestActionData(overwrites = {}) { + return { + name: `slack-${Date.now()}`, + actionTypeId: '.slack', + config: {}, + secrets: { + webhookUrl: 'https://test', + }, + ...overwrites, + }; +} diff --git a/x-pack/test/functional_with_es_ssl/lib/object_remover.ts b/x-pack/test/functional_with_es_ssl/lib/object_remover.ts new file mode 100644 index 0000000000000..28ea197db4131 --- /dev/null +++ b/x-pack/test/functional_with_es_ssl/lib/object_remover.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +interface ObjectToRemove { + id: string; + type: string; + plugin: string; +} + +export class ObjectRemover { + private readonly supertest: any; + private objectsToRemove: ObjectToRemove[] = []; + + constructor(supertest: any) { + this.supertest = supertest; + } + + add(id: ObjectToRemove['id'], type: ObjectToRemove['type'], plugin: ObjectToRemove['plugin']) { + this.objectsToRemove.push({ id, type, plugin }); + } + + async removeAll() { + await Promise.all( + this.objectsToRemove.map(({ id, type, plugin }) => { + return this.supertest + .delete(`/api/${plugin}/${type}/${id}`) + .set('kbn-xsrf', 'foo') + .expect(204); + }) + ); + this.objectsToRemove = []; + } +} diff --git a/x-pack/test/functional_with_es_ssl/page_objects/alert_details.ts b/x-pack/test/functional_with_es_ssl/page_objects/alert_details.ts index a64ab06339c9b..5c512dd5a182a 100644 --- a/x-pack/test/functional_with_es_ssl/page_objects/alert_details.ts +++ b/x-pack/test/functional_with_es_ssl/page_objects/alert_details.ts @@ -32,7 +32,7 @@ export function AlertDetailsPageProvider({ getService }: FtrProviderContext) { const $ = await table.parseDomContent(); return $.findTestSubjects('alert-instance-row') .toArray() - .map((row) => { + .map((row: CheerioElement) => { return { instance: $(row) .findTestSubject('alertInstancesTableCell-instance') @@ -86,7 +86,7 @@ export function AlertDetailsPageProvider({ getService }: FtrProviderContext) { $.findTestSubjects('alert-instance-row') .toArray() .filter( - (row) => + (row: CheerioElement) => $(row) .findTestSubject('alertInstancesTableCell-instance') .find('.euiTableCellContent') diff --git a/x-pack/test/functional_with_es_ssl/page_objects/triggers_actions_ui_page.ts b/x-pack/test/functional_with_es_ssl/page_objects/triggers_actions_ui_page.ts index db6990d2b824e..f6f21f72d2307 100644 --- a/x-pack/test/functional_with_es_ssl/page_objects/triggers_actions_ui_page.ts +++ b/x-pack/test/functional_with_es_ssl/page_objects/triggers_actions_ui_page.ts @@ -78,7 +78,7 @@ export function TriggersActionsPageProvider({ getService }: FtrProviderContext) const $ = await table.parseDomContent(); return $.findTestSubjects('connectors-row') .toArray() - .map((row) => { + .map((row: CheerioElement) => { return { name: $(row) .findTestSubject('connectorsTableCell-name') @@ -96,7 +96,7 @@ export function TriggersActionsPageProvider({ getService }: FtrProviderContext) const $ = await table.parseDomContent(); return $.findTestSubjects('alert-row') .toArray() - .map((row) => { + .map((row: CheerioElement) => { return getRowItemData(row, $); }); }, @@ -105,7 +105,7 @@ export function TriggersActionsPageProvider({ getService }: FtrProviderContext) const $ = await table.parseDomContent(); return $.findTestSubjects('alert-row') .toArray() - .map((row) => { + .map((row: CheerioElement) => { const rowItem = getRowItemData(row, $); return { ...rowItem, diff --git a/x-pack/test/functional_with_es_ssl/services/alerting/actions.ts b/x-pack/test/functional_with_es_ssl/services/alerting/actions.ts deleted file mode 100644 index 060c4f13e70c7..0000000000000 --- a/x-pack/test/functional_with_es_ssl/services/alerting/actions.ts +++ /dev/null @@ -1,49 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import axios, { AxiosInstance } from 'axios'; -import util from 'util'; -import { ToolingLog } from '@kbn/dev-utils'; - -export class Actions { - private log: ToolingLog; - private axios: AxiosInstance; - - constructor(url: string, log: ToolingLog) { - this.log = log; - this.axios = axios.create({ - headers: { 'kbn-xsrf': 'x-pack/ftr/services/alerting/actions' }, - baseURL: url, - maxRedirects: 0, - validateStatus: () => true, // we do our own validation below and throw better error messages - }); - } - - public async createAction(actionParams: { - name: string; - actionTypeId: string; - config: Record; - secrets: Record; - }) { - this.log.debug(`creating action ${actionParams.name}`); - - const { - data: action, - status: actionStatus, - statusText: actionStatusText, - } = await this.axios.post(`/api/actions/action`, actionParams); - if (actionStatus !== 200) { - throw new Error( - `Expected status code of 200, received ${actionStatus} ${actionStatusText}: ${util.inspect( - action - )}` - ); - } - - this.log.debug(`created action ${action.id}`); - return action; - } -} diff --git a/x-pack/test/functional_with_es_ssl/services/alerting/alerts.ts b/x-pack/test/functional_with_es_ssl/services/alerting/alerts.ts deleted file mode 100644 index 5ab07aa00412b..0000000000000 --- a/x-pack/test/functional_with_es_ssl/services/alerting/alerts.ts +++ /dev/null @@ -1,180 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import axios, { AxiosInstance } from 'axios'; -import util from 'util'; -import { ToolingLog } from '@kbn/dev-utils'; - -export interface AlertInstanceSummary { - status: string; - muted: boolean; - enabled: boolean; - lastRun?: string; - errorMessage?: string; - instances: Record; -} - -export interface AlertInstanceStatus { - status: string; - muted: boolean; - actionGroupId: string; - activeStartDate?: string; -} - -export class Alerts { - private log: ToolingLog; - private axios: AxiosInstance; - - constructor(url: string, log: ToolingLog) { - this.log = log; - this.axios = axios.create({ - headers: { 'kbn-xsrf': 'x-pack/ftr/services/alerting/alerts' }, - baseURL: url, - maxRedirects: 0, - validateStatus: () => true, // we do our own validation below and throw better error messages - }); - } - - public async createAlertWithActions( - name: string, - alertTypeId: string, - params?: Record, - actions?: Array<{ - id: string; - group: string; - params: Record; - }>, - tags?: string[], - consumer?: string, - schedule?: Record, - throttle?: string - ) { - this.log.debug(`creating alert ${name}`); - - const { data: alert, status, statusText } = await this.axios.post(`/api/alerts/alert`, { - enabled: true, - name, - tags, - alertTypeId, - consumer: consumer ?? 'alerts', - schedule: schedule ?? { interval: '1m' }, - throttle: throttle ?? '1m', - actions: actions ?? [], - params: params ?? {}, - }); - if (status !== 200) { - throw new Error( - `Expected status code of 200, received ${status} ${statusText}: ${util.inspect(alert)}` - ); - } - - this.log.debug(`created alert ${alert.id}`); - - return alert; - } - - public async createNoOp(name: string) { - this.log.debug(`creating alert ${name}`); - - const { data: alert, status, statusText } = await this.axios.post(`/api/alerts/alert`, { - enabled: true, - name, - tags: ['foo'], - alertTypeId: 'test.noop', - consumer: 'alerting_fixture', - schedule: { interval: '1m' }, - throttle: '1m', - actions: [], - params: {}, - }); - if (status !== 200) { - throw new Error( - `Expected status code of 200, received ${status} ${statusText}: ${util.inspect(alert)}` - ); - } - - this.log.debug(`created alert ${alert.id}`); - - return alert; - } - - public async createAlwaysFiringWithActions( - name: string, - actions: Array<{ - id: string; - group: string; - params: Record; - }>, - params: Record = {} - ) { - this.log.debug(`creating alert ${name}`); - - const { data: alert, status, statusText } = await this.axios.post(`/api/alerts/alert`, { - enabled: true, - name, - tags: ['foo'], - alertTypeId: 'test.always-firing', - consumer: 'alerts', - schedule: { interval: '1m' }, - throttle: '1m', - actions, - params, - }); - if (status !== 200) { - throw new Error( - `Expected status code of 200, received ${status} ${statusText}: ${util.inspect(alert)}` - ); - } - - this.log.debug(`created alert ${alert.id}`); - - return alert; - } - - public async createAlwaysFiringWithAction( - name: string, - action: { - id: string; - group: string; - params: Record; - } - ) { - return this.createAlwaysFiringWithActions(name, [action]); - } - - public async deleteAlert(id: string) { - this.log.debug(`deleting alert ${id}`); - - const { data: alert, status, statusText } = await this.axios.delete(`/api/alerts/alert/${id}`); - if (status !== 204) { - throw new Error( - `Expected status code of 204, received ${status} ${statusText}: ${util.inspect(alert)}` - ); - } - this.log.debug(`deleted alert ${alert.id}`); - } - - public async getAlertInstanceSummary(id: string): Promise { - this.log.debug(`getting alert ${id} state`); - - const { data } = await this.axios.get(`/api/alerts/alert/${id}/_instance_summary`); - return data; - } - - public async muteAlertInstance(id: string, instanceId: string) { - this.log.debug(`muting instance ${instanceId} under alert ${id}`); - - const { data: alert, status, statusText } = await this.axios.post( - `/api/alerts/alert/${id}/alert_instance/${instanceId}/_mute` - ); - if (status !== 204) { - throw new Error( - `Expected status code of 204, received ${status} ${statusText}: ${util.inspect(alert)}` - ); - } - this.log.debug(`muted alert instance ${instanceId}`); - } -} diff --git a/x-pack/test/functional_with_es_ssl/services/alerting/index.ts b/x-pack/test/functional_with_es_ssl/services/alerting/index.ts deleted file mode 100644 index e0aa827316c01..0000000000000 --- a/x-pack/test/functional_with_es_ssl/services/alerting/index.ts +++ /dev/null @@ -1,22 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { format as formatUrl } from 'url'; - -import { Alerts } from './alerts'; -import { Actions } from './actions'; -import { FtrProviderContext } from '../../ftr_provider_context'; - -export function AlertsServiceProvider({ getService }: FtrProviderContext) { - const log = getService('log'); - const config = getService('config'); - const url = formatUrl(config.get('servers.kibana')); - - return new (class AlertingService { - actions = new Actions(url, log); - alerts = new Alerts(url, log); - })(); -} diff --git a/x-pack/test/functional_with_es_ssl/services/index.ts b/x-pack/test/functional_with_es_ssl/services/index.ts deleted file mode 100644 index f04c2c980055d..0000000000000 --- a/x-pack/test/functional_with_es_ssl/services/index.ts +++ /dev/null @@ -1,13 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { services as xpackFunctionalServices } from '../../functional/services'; -import { AlertsServiceProvider } from './alerting'; - -export const services = { - ...xpackFunctionalServices, - alerting: AlertsServiceProvider, -}; diff --git a/x-pack/test/security_solution_cypress/es_archives/alerts/data.json.gz b/x-pack/test/security_solution_cypress/es_archives/alerts/data.json.gz deleted file mode 100644 index c0d7fb18bbdb2..0000000000000 Binary files a/x-pack/test/security_solution_cypress/es_archives/alerts/data.json.gz and /dev/null differ diff --git a/x-pack/test/security_solution_cypress/es_archives/alerts/mappings.json b/x-pack/test/security_solution_cypress/es_archives/alerts/mappings.json deleted file mode 100644 index 00a2f6fb8c8df..0000000000000 --- a/x-pack/test/security_solution_cypress/es_archives/alerts/mappings.json +++ /dev/null @@ -1,8124 +0,0 @@ -{ - "type": "index", - "value": { - "aliases": { - ".siem-signals-default": { - "is_write_index": true - } - }, - "index": ".siem-signals-default-000001", - "mappings": { - "dynamic": "false", - "_meta": { - "version": 3 - }, - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "properties": { - "ephemeral_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "client": { - "properties": { - "address": { - "type": "keyword", - "ignore_above": 1024 - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "availability_zone": { - "type": "keyword", - "ignore_above": 1024 - }, - "instance": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "machine": { - "properties": { - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "provider": { - "type": "keyword", - "ignore_above": 1024 - }, - "region": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "container": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "image": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "tag": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "runtime": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "destination": { - "properties": { - "address": { - "type": "keyword", - "ignore_above": 1024 - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "dll": { - "properties": { - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024 - }, - "pe": { - "properties": { - "company": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "file_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "original_file_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "type": "keyword", - "ignore_above": 1024 - }, - "data": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "ttl": { - "type": "long" - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "header_flags": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "op_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "question": { - "properties": { - "class": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "subdomain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ecs": { - "properties": { - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "error": { - "properties": { - "code": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "message": { - "type": "text", - "norms": false - }, - "stack_trace": { - "type": "keyword", - "index": false, - "doc_values": false, - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "event": { - "properties": { - "action": { - "type": "keyword", - "ignore_above": 1024 - }, - "category": { - "type": "keyword", - "ignore_above": 1024 - }, - "code": { - "type": "keyword", - "ignore_above": 1024 - }, - "created": { - "type": "date" - }, - "dataset": { - "type": "keyword", - "ignore_above": 1024 - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "ingested": { - "type": "date" - }, - "kind": { - "type": "keyword", - "ignore_above": 1024 - }, - "module": { - "type": "keyword", - "ignore_above": 1024 - }, - "original": { - "type": "keyword", - "index": false, - "doc_values": false, - "ignore_above": 1024 - }, - "outcome": { - "type": "keyword", - "ignore_above": 1024 - }, - "provider": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "url": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "type": "keyword", - "ignore_above": 1024 - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "type": "keyword", - "ignore_above": 1024 - }, - "directory": { - "type": "keyword", - "ignore_above": 1024 - }, - "drive_letter": { - "type": "keyword", - "ignore_above": 1 - }, - "extension": { - "type": "keyword", - "ignore_above": 1024 - }, - "gid": { - "type": "keyword", - "ignore_above": 1024 - }, - "group": { - "type": "keyword", - "ignore_above": 1024 - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "inode": { - "type": "keyword", - "ignore_above": 1024 - }, - "mime_type": { - "type": "keyword", - "ignore_above": 1024 - }, - "mode": { - "type": "keyword", - "ignore_above": 1024 - }, - "mtime": { - "type": "date" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "owner": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "pe": { - "properties": { - "company": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "file_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "original_file_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "size": { - "type": "long" - }, - "target_path": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "uid": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "host": { - "properties": { - "architecture": { - "type": "keyword", - "ignore_above": 1024 - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hostname": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "os": { - "properties": { - "family": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "kernel": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "platform": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "http": { - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "method": { - "type": "keyword", - "ignore_above": 1024 - }, - "referrer": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "interface": { - "properties": { - "alias": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "labels": { - "type": "object" - }, - "log": { - "properties": { - "level": { - "type": "keyword", - "ignore_above": 1024 - }, - "logger": { - "type": "keyword", - "ignore_above": 1024 - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "integer" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "function": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "original": { - "type": "keyword", - "index": false, - "doc_values": false, - "ignore_above": 1024 - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - } - } - }, - "message": { - "type": "text", - "norms": false - }, - "network": { - "properties": { - "application": { - "type": "keyword", - "ignore_above": 1024 - }, - "bytes": { - "type": "long" - }, - "community_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "direction": { - "type": "keyword", - "ignore_above": 1024 - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "type": "keyword", - "ignore_above": 1024 - }, - "inner": { - "properties": { - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "packets": { - "type": "long" - }, - "protocol": { - "type": "keyword", - "ignore_above": 1024 - }, - "transport": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "observer": { - "properties": { - "egress": { - "properties": { - "interface": { - "properties": { - "alias": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "zone": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hostname": { - "type": "keyword", - "ignore_above": 1024 - }, - "ingress": { - "properties": { - "interface": { - "properties": { - "alias": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "zone": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "os": { - "properties": { - "family": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "kernel": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "platform": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - }, - "serial_number": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "vendor": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "organization": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "os": { - "properties": { - "family": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "kernel": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "platform": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "package": { - "properties": { - "architecture": { - "type": "keyword", - "ignore_above": 1024 - }, - "build_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "checksum": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "install_scope": { - "type": "keyword", - "ignore_above": 1024 - }, - "installed": { - "type": "date" - }, - "license": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - }, - "size": { - "type": "long" - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "pe": { - "properties": { - "company": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "file_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "original_file_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "process": { - "properties": { - "args": { - "type": "keyword", - "ignore_above": 1024 - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "entity_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "executable": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "parent": { - "properties": { - "args": { - "type": "keyword", - "ignore_above": 1024 - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "entity_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "executable": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "title": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "pe": { - "properties": { - "company": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "file_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "original_file_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "title": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "type": "keyword", - "ignore_above": 1024 - }, - "strings": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hive": { - "type": "keyword", - "ignore_above": 1024 - }, - "key": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024 - }, - "value": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "related": { - "properties": { - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "ip": { - "type": "ip" - }, - "user": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "rule": { - "properties": { - "author": { - "type": "keyword", - "ignore_above": 1024 - }, - "category": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "license": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - }, - "ruleset": { - "type": "keyword", - "ignore_above": 1024 - }, - "uuid": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "server": { - "properties": { - "address": { - "type": "keyword", - "ignore_above": 1024 - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "service": { - "properties": { - "ephemeral_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "node": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "state": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "signal": { - "properties": { - "ancestors": { - "properties": { - "depth": { - "type": "long" - }, - "id": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "rule": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "depth": { - "type": "integer" - }, - "group": { - "properties": { - "id": { - "type": "keyword" - }, - "index": { - "type": "integer" - } - } - }, - "original_event": { - "properties": { - "action": { - "type": "keyword" - }, - "category": { - "type": "keyword" - }, - "code": { - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "kind": { - "type": "keyword" - }, - "module": { - "type": "keyword" - }, - "original": { - "type": "keyword", - "index": false, - "doc_values": false - }, - "outcome": { - "type": "keyword" - }, - "provider": { - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "original_signal": { - "type": "object", - "dynamic": "false", - "enabled": false - }, - "original_time": { - "type": "date" - }, - "parent": { - "properties": { - "depth": { - "type": "long" - }, - "id": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "rule": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "parents": { - "properties": { - "depth": { - "type": "long" - }, - "id": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "rule": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "author": { - "type": "keyword" - }, - "building_block_type": { - "type": "keyword" - }, - "created_at": { - "type": "date" - }, - "created_by": { - "type": "keyword" - }, - "description": { - "type": "keyword" - }, - "enabled": { - "type": "keyword" - }, - "false_positives": { - "type": "keyword" - }, - "filters": { - "type": "object" - }, - "from": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "immutable": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "interval": { - "type": "keyword" - }, - "language": { - "type": "keyword" - }, - "license": { - "type": "keyword" - }, - "max_signals": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "note": { - "type": "text" - }, - "output_index": { - "type": "keyword" - }, - "query": { - "type": "keyword" - }, - "references": { - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_mapping": { - "properties": { - "field": { - "type": "keyword" - }, - "operator": { - "type": "keyword" - }, - "value": { - "type": "keyword" - } - } - }, - "rule_id": { - "type": "keyword" - }, - "rule_name_override": { - "type": "keyword" - }, - "saved_id": { - "type": "keyword" - }, - "severity": { - "type": "keyword" - }, - "severity_mapping": { - "properties": { - "field": { - "type": "keyword" - }, - "operator": { - "type": "keyword" - }, - "severity": { - "type": "keyword" - }, - "value": { - "type": "keyword" - } - } - }, - "size": { - "type": "keyword" - }, - "tags": { - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "reference": { - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "reference": { - "type": "keyword" - } - } - } - } - }, - "threshold": { - "properties": { - "field": { - "type": "keyword" - }, - "value": { - "type": "float" - } - } - }, - "timeline_id": { - "type": "keyword" - }, - "timeline_title": { - "type": "keyword" - }, - "timestamp_override": { - "type": "keyword" - }, - "to": { - "type": "keyword" - }, - "type": { - "type": "keyword" - }, - "updated_at": { - "type": "date" - }, - "updated_by": { - "type": "keyword" - }, - "version": { - "type": "keyword" - } - } - }, - "status": { - "type": "keyword" - }, - "threshold_count": { - "type": "float" - }, - "threshold_result": { - "properties": { - "count": { - "type": "long" - }, - "value": { - "type": "keyword" - } - } - } - } - }, - "source": { - "properties": { - "address": { - "type": "keyword", - "ignore_above": 1024 - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "tags": { - "type": "keyword", - "ignore_above": 1024 - }, - "threat": { - "properties": { - "framework": { - "type": "keyword", - "ignore_above": 1024 - }, - "tactic": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "technique": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "tls": { - "properties": { - "cipher": { - "type": "keyword", - "ignore_above": 1024 - }, - "client": { - "properties": { - "certificate": { - "type": "keyword", - "ignore_above": 1024 - }, - "certificate_chain": { - "type": "keyword", - "ignore_above": 1024 - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "issuer": { - "type": "keyword", - "ignore_above": 1024 - }, - "ja3": { - "type": "keyword", - "ignore_above": 1024 - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject": { - "type": "keyword", - "ignore_above": 1024 - }, - "supported_ciphers": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "curve": { - "type": "keyword", - "ignore_above": 1024 - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "type": "keyword", - "ignore_above": 1024 - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "type": "keyword", - "ignore_above": 1024 - }, - "certificate_chain": { - "type": "keyword", - "ignore_above": 1024 - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "issuer": { - "type": "keyword", - "ignore_above": 1024 - }, - "ja3s": { - "type": "keyword", - "ignore_above": 1024 - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - }, - "version_protocol": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "trace": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "transaction": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "url": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "extension": { - "type": "keyword", - "ignore_above": 1024 - }, - "fragment": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "original": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "password": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024 - }, - "port": { - "type": "long" - }, - "query": { - "type": "keyword", - "ignore_above": 1024 - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "scheme": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "username": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "user_agent": { - "properties": { - "device": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "original": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "os": { - "properties": { - "family": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "kernel": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "platform": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "vulnerability": { - "properties": { - "category": { - "type": "keyword", - "ignore_above": 1024 - }, - "classification": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "enumeration": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - }, - "report_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "scanner": { - "properties": { - "vendor": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "severity": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "settings": { - "index": { - "lifecycle": { - "name": ".siem-signals-default", - "rollover_alias": ".siem-signals-default" - }, - "number_of_replicas": "1", - "number_of_shards": "1" - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - "auditbeat-7.6.0": { - "is_write_index": true - } - }, - "index": "auditbeat-7.6.0-2020.03.11-000001", - "mappings": { - "_meta": { - "beat": "auditbeat", - "version": "7.6.0" - }, - "date_detection": false, - "dynamic_templates": [ - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "container.labels.*" - } - }, - { - "dns.answers": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "dns.answers.*" - } - }, - { - "log.syslog": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "log.syslog.*" - } - }, - { - "fields": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "fields.*" - } - }, - { - "docker.container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "docker.container.labels.*" - } - }, - { - "kubernetes.labels.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.labels.*" - } - }, - { - "kubernetes.annotations.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.annotations.*" - } - }, - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "auditd": { - "properties": { - "data": { - "properties": { - "a0": { - "ignore_above": 1024, - "type": "keyword" - }, - "a1": { - "ignore_above": 1024, - "type": "keyword" - }, - "a2": { - "ignore_above": 1024, - "type": "keyword" - }, - "a3": { - "ignore_above": 1024, - "type": "keyword" - }, - "a[0-3]": { - "ignore_above": 1024, - "type": "keyword" - }, - "acct": { - "ignore_above": 1024, - "type": "keyword" - }, - "acl": { - "ignore_above": 1024, - "type": "keyword" - }, - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "added": { - "ignore_above": 1024, - "type": "keyword" - }, - "addr": { - "ignore_above": 1024, - "type": "keyword" - }, - "apparmor": { - "ignore_above": 1024, - "type": "keyword" - }, - "arch": { - "ignore_above": 1024, - "type": "keyword" - }, - "argc": { - "ignore_above": 1024, - "type": "keyword" - }, - "audit_backlog_limit": { - "ignore_above": 1024, - "type": "keyword" - }, - "audit_backlog_wait_time": { - "ignore_above": 1024, - "type": "keyword" - }, - "audit_enabled": { - "ignore_above": 1024, - "type": "keyword" - }, - "audit_failure": { - "ignore_above": 1024, - "type": "keyword" - }, - "banners": { - "ignore_above": 1024, - "type": "keyword" - }, - "bool": { - "ignore_above": 1024, - "type": "keyword" - }, - "bus": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_fe": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_fi": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_fp": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_fver": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_pe": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_pi": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_pp": { - "ignore_above": 1024, - "type": "keyword" - }, - "capability": { - "ignore_above": 1024, - "type": "keyword" - }, - "cgroup": { - "ignore_above": 1024, - "type": "keyword" - }, - "changed": { - "ignore_above": 1024, - "type": "keyword" - }, - "cipher": { - "ignore_above": 1024, - "type": "keyword" - }, - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "cmd": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "compat": { - "ignore_above": 1024, - "type": "keyword" - }, - "daddr": { - "ignore_above": 1024, - "type": "keyword" - }, - "data": { - "ignore_above": 1024, - "type": "keyword" - }, - "default-context": { - "ignore_above": 1024, - "type": "keyword" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "dir": { - "ignore_above": 1024, - "type": "keyword" - }, - "direction": { - "ignore_above": 1024, - "type": "keyword" - }, - "dmac": { - "ignore_above": 1024, - "type": "keyword" - }, - "dport": { - "ignore_above": 1024, - "type": "keyword" - }, - "enforcing": { - "ignore_above": 1024, - "type": "keyword" - }, - "entries": { - "ignore_above": 1024, - "type": "keyword" - }, - "exit": { - "ignore_above": 1024, - "type": "keyword" - }, - "fam": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "fd": { - "ignore_above": 1024, - "type": "keyword" - }, - "fe": { - "ignore_above": 1024, - "type": "keyword" - }, - "feature": { - "ignore_above": 1024, - "type": "keyword" - }, - "fi": { - "ignore_above": 1024, - "type": "keyword" - }, - "file": { - "ignore_above": 1024, - "type": "keyword" - }, - "flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "format": { - "ignore_above": 1024, - "type": "keyword" - }, - "fp": { - "ignore_above": 1024, - "type": "keyword" - }, - "fver": { - "ignore_above": 1024, - "type": "keyword" - }, - "grantors": { - "ignore_above": 1024, - "type": "keyword" - }, - "grp": { - "ignore_above": 1024, - "type": "keyword" - }, - "hook": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "icmp_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "igid": { - "ignore_above": 1024, - "type": "keyword" - }, - "img-ctx": { - "ignore_above": 1024, - "type": "keyword" - }, - "inif": { - "ignore_above": 1024, - "type": "keyword" - }, - "ino": { - "ignore_above": 1024, - "type": "keyword" - }, - "inode_gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "inode_uid": { - "ignore_above": 1024, - "type": "keyword" - }, - "invalid_context": { - "ignore_above": 1024, - "type": "keyword" - }, - "ioctlcmd": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "ignore_above": 1024, - "type": "keyword" - }, - "ipid": { - "ignore_above": 1024, - "type": "keyword" - }, - "ipx-net": { - "ignore_above": 1024, - "type": "keyword" - }, - "items": { - "ignore_above": 1024, - "type": "keyword" - }, - "iuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "ksize": { - "ignore_above": 1024, - "type": "keyword" - }, - "laddr": { - "ignore_above": 1024, - "type": "keyword" - }, - "len": { - "ignore_above": 1024, - "type": "keyword" - }, - "list": { - "ignore_above": 1024, - "type": "keyword" - }, - "lport": { - "ignore_above": 1024, - "type": "keyword" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "macproto": { - "ignore_above": 1024, - "type": "keyword" - }, - "maj": { - "ignore_above": 1024, - "type": "keyword" - }, - "major": { - "ignore_above": 1024, - "type": "keyword" - }, - "minor": { - "ignore_above": 1024, - "type": "keyword" - }, - "model": { - "ignore_above": 1024, - "type": "keyword" - }, - "msg": { - "ignore_above": 1024, - "type": "keyword" - }, - "nargs": { - "ignore_above": 1024, - "type": "keyword" - }, - "net": { - "ignore_above": 1024, - "type": "keyword" - }, - "new": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-chardev": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-disk": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-enabled": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-fs": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-level": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-log_passwd": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-mem": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-net": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-range": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-rng": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-role": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-seuser": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-vcpu": { - "ignore_above": 1024, - "type": "keyword" - }, - "new_gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "new_lock": { - "ignore_above": 1024, - "type": "keyword" - }, - "new_pe": { - "ignore_above": 1024, - "type": "keyword" - }, - "new_pi": { - "ignore_above": 1024, - "type": "keyword" - }, - "new_pp": { - "ignore_above": 1024, - "type": "keyword" - }, - "nlnk-fam": { - "ignore_above": 1024, - "type": "keyword" - }, - "nlnk-grp": { - "ignore_above": 1024, - "type": "keyword" - }, - "nlnk-pid": { - "ignore_above": 1024, - "type": "keyword" - }, - "oauid": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_uid": { - "ignore_above": 1024, - "type": "keyword" - }, - "ocomm": { - "ignore_above": 1024, - "type": "keyword" - }, - "oflag": { - "ignore_above": 1024, - "type": "keyword" - }, - "old": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-auid": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-chardev": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-disk": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-enabled": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-fs": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-level": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-log_passwd": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-mem": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-net": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-range": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-rng": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-role": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-ses": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-seuser": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-vcpu": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_enforcing": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_lock": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_pe": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_pi": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_pp": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_prom": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_val": { - "ignore_above": 1024, - "type": "keyword" - }, - "op": { - "ignore_above": 1024, - "type": "keyword" - }, - "opid": { - "ignore_above": 1024, - "type": "keyword" - }, - "oses": { - "ignore_above": 1024, - "type": "keyword" - }, - "outif": { - "ignore_above": 1024, - "type": "keyword" - }, - "parent": { - "ignore_above": 1024, - "type": "keyword" - }, - "per": { - "ignore_above": 1024, - "type": "keyword" - }, - "perm": { - "ignore_above": 1024, - "type": "keyword" - }, - "perm_mask": { - "ignore_above": 1024, - "type": "keyword" - }, - "permissive": { - "ignore_above": 1024, - "type": "keyword" - }, - "pfs": { - "ignore_above": 1024, - "type": "keyword" - }, - "printer": { - "ignore_above": 1024, - "type": "keyword" - }, - "prom": { - "ignore_above": 1024, - "type": "keyword" - }, - "proto": { - "ignore_above": 1024, - "type": "keyword" - }, - "qbytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "range": { - "ignore_above": 1024, - "type": "keyword" - }, - "reason": { - "ignore_above": 1024, - "type": "keyword" - }, - "removed": { - "ignore_above": 1024, - "type": "keyword" - }, - "res": { - "ignore_above": 1024, - "type": "keyword" - }, - "resrc": { - "ignore_above": 1024, - "type": "keyword" - }, - "rport": { - "ignore_above": 1024, - "type": "keyword" - }, - "sauid": { - "ignore_above": 1024, - "type": "keyword" - }, - "scontext": { - "ignore_above": 1024, - "type": "keyword" - }, - "selected-context": { - "ignore_above": 1024, - "type": "keyword" - }, - "seperm": { - "ignore_above": 1024, - "type": "keyword" - }, - "seperms": { - "ignore_above": 1024, - "type": "keyword" - }, - "seqno": { - "ignore_above": 1024, - "type": "keyword" - }, - "seresult": { - "ignore_above": 1024, - "type": "keyword" - }, - "ses": { - "ignore_above": 1024, - "type": "keyword" - }, - "seuser": { - "ignore_above": 1024, - "type": "keyword" - }, - "sig": { - "ignore_above": 1024, - "type": "keyword" - }, - "sigev_signo": { - "ignore_above": 1024, - "type": "keyword" - }, - "smac": { - "ignore_above": 1024, - "type": "keyword" - }, - "socket": { - "properties": { - "addr": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "ignore_above": 1024, - "type": "keyword" - }, - "saddr": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "spid": { - "ignore_above": 1024, - "type": "keyword" - }, - "sport": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "subj": { - "ignore_above": 1024, - "type": "keyword" - }, - "success": { - "ignore_above": 1024, - "type": "keyword" - }, - "syscall": { - "ignore_above": 1024, - "type": "keyword" - }, - "table": { - "ignore_above": 1024, - "type": "keyword" - }, - "tclass": { - "ignore_above": 1024, - "type": "keyword" - }, - "tcontext": { - "ignore_above": 1024, - "type": "keyword" - }, - "terminal": { - "ignore_above": 1024, - "type": "keyword" - }, - "tty": { - "ignore_above": 1024, - "type": "keyword" - }, - "unit": { - "ignore_above": 1024, - "type": "keyword" - }, - "uri": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "val": { - "ignore_above": 1024, - "type": "keyword" - }, - "ver": { - "ignore_above": 1024, - "type": "keyword" - }, - "virt": { - "ignore_above": 1024, - "type": "keyword" - }, - "vm": { - "ignore_above": 1024, - "type": "keyword" - }, - "vm-ctx": { - "ignore_above": 1024, - "type": "keyword" - }, - "vm-pid": { - "ignore_above": 1024, - "type": "keyword" - }, - "watch": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "message_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "paths": { - "properties": { - "dev": { - "ignore_above": 1024, - "type": "keyword" - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "item": { - "ignore_above": 1024, - "type": "keyword" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "nametype": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_level": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_role": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_user": { - "ignore_above": 1024, - "type": "keyword" - }, - "objtype": { - "ignore_above": 1024, - "type": "keyword" - }, - "ogid": { - "ignore_above": 1024, - "type": "keyword" - }, - "ouid": { - "ignore_above": 1024, - "type": "keyword" - }, - "rdev": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "result": { - "ignore_above": 1024, - "type": "keyword" - }, - "sequence": { - "type": "long" - }, - "session": { - "ignore_above": 1024, - "type": "keyword" - }, - "summary": { - "properties": { - "actor": { - "properties": { - "primary": { - "ignore_above": 1024, - "type": "keyword" - }, - "secondary": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "how": { - "ignore_above": 1024, - "type": "keyword" - }, - "object": { - "properties": { - "primary": { - "ignore_above": 1024, - "type": "keyword" - }, - "secondary": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "client": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "availability_zone": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "instance": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "machine": { - "properties": { - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "project": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "region": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "container": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "tag": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "runtime": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "destination": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "data": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ttl": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "header_flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "op_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "question": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "subdomain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "docker": { - "properties": { - "container": { - "properties": { - "labels": { - "type": "object" - } - } - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "norms": false, - "type": "text" - }, - "stack_trace": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "event": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingested": { - "type": "date" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "outcome": { - "ignore_above": 1024, - "type": "keyword" - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "fields": { - "type": "object" - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "directory": { - "ignore_above": 1024, - "type": "keyword" - }, - "drive_letter": { - "ignore_above": 1, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mtime": { - "type": "date" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "fields": { - "raw": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "owner": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "selinux": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "role": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "setgid": { - "type": "boolean" - }, - "setuid": { - "type": "boolean" - }, - "size": { - "type": "long" - }, - "target_path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geoip": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "properties": { - "blake2b_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "blake2b_384": { - "ignore_above": 1024, - "type": "keyword" - }, - "blake2b_512": { - "ignore_above": 1024, - "type": "keyword" - }, - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha384": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_384": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_512": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512_224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "xxh64": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "containerized": { - "type": "boolean" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "build": { - "ignore_above": 1024, - "type": "keyword" - }, - "codename": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "http": { - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "referrer": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "jolokia": { - "properties": { - "agent": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "secured": { - "type": "boolean" - }, - "server": { - "properties": { - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kubernetes": { - "properties": { - "annotations": { - "properties": { - "*": { - "type": "object" - } - } - }, - "container": { - "properties": { - "image": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "deployment": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "properties": { - "*": { - "type": "object" - } - } - }, - "namespace": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pod": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "replicaset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "statefulset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "labels": { - "type": "object" - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "network": { - "properties": { - "application": { - "ignore_above": 1024, - "type": "keyword" - }, - "bytes": { - "type": "long" - }, - "community_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "direction": { - "ignore_above": 1024, - "type": "keyword" - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "packets": { - "type": "long" - }, - "protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "observer": { - "properties": { - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "organization": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "package": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "build_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "checksum": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "install_scope": { - "ignore_above": 1024, - "type": "keyword" - }, - "installed": { - "type": "date" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "process": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "blake2b_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "blake2b_384": { - "ignore_above": 1024, - "type": "keyword" - }, - "blake2b_512": { - "ignore_above": 1024, - "type": "keyword" - }, - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha384": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_384": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_512": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512_224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "xxh64": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "parent": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "strings": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hive": { - "ignore_above": 1024, - "type": "keyword" - }, - "key": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "related": { - "properties": { - "ip": { - "type": "ip" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "ruleset": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "server": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "service": { - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "socket": { - "properties": { - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "system": { - "properties": { - "audit": { - "properties": { - "host": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "boottime": { - "type": "date" - }, - "containerized": { - "type": "boolean" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "codename": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timezone": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "offset": { - "properties": { - "sec": { - "type": "long" - } - } - } - } - }, - "uptime": { - "type": "long" - } - } - }, - "package": { - "properties": { - "arch": { - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "installtime": { - "type": "date" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "release": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "summary": { - "ignore_above": 1024, - "type": "keyword" - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user": { - "properties": { - "dir": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "type": "object" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "password": { - "properties": { - "last_changed": { - "type": "date" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "shell": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - }, - "user_information": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "tags": { - "ignore_above": 1024, - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "ignore_above": 1024, - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "timeseries": { - "properties": { - "instance": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tls": { - "properties": { - "cipher": { - "ignore_above": 1024, - "type": "keyword" - }, - "client": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - }, - "supported_ciphers": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "curve": { - "ignore_above": 1024, - "type": "keyword" - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3s": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_protocol": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tracing": { - "properties": { - "trace": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "transaction": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "url": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "fragment": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "password": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "query": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "scheme": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "username": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user": { - "properties": { - "audit": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "effective": { - "properties": { - "group": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "filesystem": { - "properties": { - "group": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "name_map": { - "type": "object" - }, - "saved": { - "properties": { - "group": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "selinux": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "role": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "terminal": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user_agent": { - "properties": { - "device": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vulnerability": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "classification": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "enumeration": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "report_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "scanner": { - "properties": { - "vendor": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "severity": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "settings": { - "index": { - "lifecycle": { - "name": "auditbeat", - "rollover_alias": "auditbeat-7.6.0" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "number_of_replicas": "1", - "number_of_shards": "1", - "query": { - "default_field": [ - "message", - "tags", - "agent.ephemeral_id", - "agent.id", - "agent.name", - "agent.type", - "agent.version", - "as.organization.name", - "client.address", - "client.as.organization.name", - "client.domain", - "client.geo.city_name", - "client.geo.continent_name", - "client.geo.country_iso_code", - "client.geo.country_name", - "client.geo.name", - "client.geo.region_iso_code", - "client.geo.region_name", - "client.mac", - "client.registered_domain", - "client.top_level_domain", - "client.user.domain", - "client.user.email", - "client.user.full_name", - "client.user.group.domain", - "client.user.group.id", - "client.user.group.name", - "client.user.hash", - "client.user.id", - "client.user.name", - "cloud.account.id", - "cloud.availability_zone", - "cloud.instance.id", - "cloud.instance.name", - "cloud.machine.type", - "cloud.provider", - "cloud.region", - "container.id", - "container.image.name", - "container.image.tag", - "container.name", - "container.runtime", - "destination.address", - "destination.as.organization.name", - "destination.domain", - "destination.geo.city_name", - "destination.geo.continent_name", - "destination.geo.country_iso_code", - "destination.geo.country_name", - "destination.geo.name", - "destination.geo.region_iso_code", - "destination.geo.region_name", - "destination.mac", - "destination.registered_domain", - "destination.top_level_domain", - "destination.user.domain", - "destination.user.email", - "destination.user.full_name", - "destination.user.group.domain", - "destination.user.group.id", - "destination.user.group.name", - "destination.user.hash", - "destination.user.id", - "destination.user.name", - "dns.answers.class", - "dns.answers.data", - "dns.answers.name", - "dns.answers.type", - "dns.header_flags", - "dns.id", - "dns.op_code", - "dns.question.class", - "dns.question.name", - "dns.question.registered_domain", - "dns.question.subdomain", - "dns.question.top_level_domain", - "dns.question.type", - "dns.response_code", - "dns.type", - "ecs.version", - "error.code", - "error.id", - "error.message", - "error.stack_trace", - "error.type", - "event.action", - "event.category", - "event.code", - "event.dataset", - "event.hash", - "event.id", - "event.kind", - "event.module", - "event.original", - "event.outcome", - "event.provider", - "event.timezone", - "event.type", - "file.device", - "file.directory", - "file.extension", - "file.gid", - "file.group", - "file.hash.md5", - "file.hash.sha1", - "file.hash.sha256", - "file.hash.sha512", - "file.inode", - "file.mode", - "file.name", - "file.owner", - "file.path", - "file.target_path", - "file.type", - "file.uid", - "geo.city_name", - "geo.continent_name", - "geo.country_iso_code", - "geo.country_name", - "geo.name", - "geo.region_iso_code", - "geo.region_name", - "group.domain", - "group.id", - "group.name", - "hash.md5", - "hash.sha1", - "hash.sha256", - "hash.sha512", - "host.architecture", - "host.geo.city_name", - "host.geo.continent_name", - "host.geo.country_iso_code", - "host.geo.country_name", - "host.geo.name", - "host.geo.region_iso_code", - "host.geo.region_name", - "host.hostname", - "host.id", - "host.mac", - "host.name", - "host.os.family", - "host.os.full", - "host.os.kernel", - "host.os.name", - "host.os.platform", - "host.os.version", - "host.type", - "host.user.domain", - "host.user.email", - "host.user.full_name", - "host.user.group.domain", - "host.user.group.id", - "host.user.group.name", - "host.user.hash", - "host.user.id", - "host.user.name", - "http.request.body.content", - "http.request.method", - "http.request.referrer", - "http.response.body.content", - "http.version", - "log.level", - "log.logger", - "log.origin.file.name", - "log.origin.function", - "log.original", - "log.syslog.facility.name", - "log.syslog.severity.name", - "network.application", - "network.community_id", - "network.direction", - "network.iana_number", - "network.name", - "network.protocol", - "network.transport", - "network.type", - "observer.geo.city_name", - "observer.geo.continent_name", - "observer.geo.country_iso_code", - "observer.geo.country_name", - "observer.geo.name", - "observer.geo.region_iso_code", - "observer.geo.region_name", - "observer.hostname", - "observer.mac", - "observer.name", - "observer.os.family", - "observer.os.full", - "observer.os.kernel", - "observer.os.name", - "observer.os.platform", - "observer.os.version", - "observer.product", - "observer.serial_number", - "observer.type", - "observer.vendor", - "observer.version", - "organization.id", - "organization.name", - "os.family", - "os.full", - "os.kernel", - "os.name", - "os.platform", - "os.version", - "package.architecture", - "package.checksum", - "package.description", - "package.install_scope", - "package.license", - "package.name", - "package.path", - "package.version", - "process.args", - "text", - "process.executable", - "process.hash.md5", - "process.hash.sha1", - "process.hash.sha256", - "process.hash.sha512", - "process.name", - "text", - "text", - "text", - "text", - "text", - "process.thread.name", - "process.title", - "process.working_directory", - "server.address", - "server.as.organization.name", - "server.domain", - "server.geo.city_name", - "server.geo.continent_name", - "server.geo.country_iso_code", - "server.geo.country_name", - "server.geo.name", - "server.geo.region_iso_code", - "server.geo.region_name", - "server.mac", - "server.registered_domain", - "server.top_level_domain", - "server.user.domain", - "server.user.email", - "server.user.full_name", - "server.user.group.domain", - "server.user.group.id", - "server.user.group.name", - "server.user.hash", - "server.user.id", - "server.user.name", - "service.ephemeral_id", - "service.id", - "service.name", - "service.node.name", - "service.state", - "service.type", - "service.version", - "source.address", - "source.as.organization.name", - "source.domain", - "source.geo.city_name", - "source.geo.continent_name", - "source.geo.country_iso_code", - "source.geo.country_name", - "source.geo.name", - "source.geo.region_iso_code", - "source.geo.region_name", - "source.mac", - "source.registered_domain", - "source.top_level_domain", - "source.user.domain", - "source.user.email", - "source.user.full_name", - "source.user.group.domain", - "source.user.group.id", - "source.user.group.name", - "source.user.hash", - "source.user.id", - "source.user.name", - "threat.framework", - "threat.tactic.id", - "threat.tactic.name", - "threat.tactic.reference", - "threat.technique.id", - "threat.technique.name", - "threat.technique.reference", - "tracing.trace.id", - "tracing.transaction.id", - "url.domain", - "url.extension", - "url.fragment", - "url.full", - "url.original", - "url.password", - "url.path", - "url.query", - "url.registered_domain", - "url.scheme", - "url.top_level_domain", - "url.username", - "user.domain", - "user.email", - "user.full_name", - "user.group.domain", - "user.group.id", - "user.group.name", - "user.hash", - "user.id", - "user.name", - "user_agent.device.name", - "user_agent.name", - "text", - "user_agent.original", - "user_agent.os.family", - "user_agent.os.full", - "user_agent.os.kernel", - "user_agent.os.name", - "user_agent.os.platform", - "user_agent.os.version", - "user_agent.version", - "text", - "agent.hostname", - "timeseries.instance", - "cloud.project.id", - "cloud.image.id", - "host.os.build", - "host.os.codename", - "kubernetes.pod.name", - "kubernetes.pod.uid", - "kubernetes.namespace", - "kubernetes.node.name", - "kubernetes.replicaset.name", - "kubernetes.deployment.name", - "kubernetes.statefulset.name", - "kubernetes.container.name", - "kubernetes.container.image", - "jolokia.agent.version", - "jolokia.agent.id", - "jolokia.server.product", - "jolokia.server.version", - "jolokia.server.vendor", - "jolokia.url", - "raw", - "file.origin", - "file.selinux.user", - "file.selinux.role", - "file.selinux.domain", - "file.selinux.level", - "user.audit.id", - "user.audit.name", - "user.effective.id", - "user.effective.name", - "user.effective.group.id", - "user.effective.group.name", - "user.filesystem.id", - "user.filesystem.name", - "user.filesystem.group.id", - "user.filesystem.group.name", - "user.saved.id", - "user.saved.name", - "user.saved.group.id", - "user.saved.group.name", - "user.selinux.user", - "user.selinux.role", - "user.selinux.domain", - "user.selinux.level", - "user.selinux.category", - "source.path", - "destination.path", - "auditd.message_type", - "auditd.session", - "auditd.result", - "auditd.summary.actor.primary", - "auditd.summary.actor.secondary", - "auditd.summary.object.type", - "auditd.summary.object.primary", - "auditd.summary.object.secondary", - "auditd.summary.how", - "auditd.paths.inode", - "auditd.paths.dev", - "auditd.paths.obj_user", - "auditd.paths.obj_role", - "auditd.paths.obj_domain", - "auditd.paths.obj_level", - "auditd.paths.objtype", - "auditd.paths.ouid", - "auditd.paths.rdev", - "auditd.paths.nametype", - "auditd.paths.ogid", - "auditd.paths.item", - "auditd.paths.mode", - "auditd.paths.name", - "auditd.data.action", - "auditd.data.minor", - "auditd.data.acct", - "auditd.data.addr", - "auditd.data.cipher", - "auditd.data.id", - "auditd.data.entries", - "auditd.data.kind", - "auditd.data.ksize", - "auditd.data.spid", - "auditd.data.arch", - "auditd.data.argc", - "auditd.data.major", - "auditd.data.unit", - "auditd.data.table", - "auditd.data.terminal", - "auditd.data.grantors", - "auditd.data.direction", - "auditd.data.op", - "auditd.data.tty", - "auditd.data.syscall", - "auditd.data.data", - "auditd.data.family", - "auditd.data.mac", - "auditd.data.pfs", - "auditd.data.items", - "auditd.data.a0", - "auditd.data.a1", - "auditd.data.a2", - "auditd.data.a3", - "auditd.data.hostname", - "auditd.data.lport", - "auditd.data.rport", - "auditd.data.exit", - "auditd.data.fp", - "auditd.data.laddr", - "auditd.data.sport", - "auditd.data.capability", - "auditd.data.nargs", - "auditd.data.new-enabled", - "auditd.data.audit_backlog_limit", - "auditd.data.dir", - "auditd.data.cap_pe", - "auditd.data.model", - "auditd.data.new_pp", - "auditd.data.old-enabled", - "auditd.data.oauid", - "auditd.data.old", - "auditd.data.banners", - "auditd.data.feature", - "auditd.data.vm-ctx", - "auditd.data.opid", - "auditd.data.seperms", - "auditd.data.seresult", - "auditd.data.new-rng", - "auditd.data.old-net", - "auditd.data.sigev_signo", - "auditd.data.ino", - "auditd.data.old_enforcing", - "auditd.data.old-vcpu", - "auditd.data.range", - "auditd.data.res", - "auditd.data.added", - "auditd.data.fam", - "auditd.data.nlnk-pid", - "auditd.data.subj", - "auditd.data.a[0-3]", - "auditd.data.cgroup", - "auditd.data.kernel", - "auditd.data.ocomm", - "auditd.data.new-net", - "auditd.data.permissive", - "auditd.data.class", - "auditd.data.compat", - "auditd.data.fi", - "auditd.data.changed", - "auditd.data.msg", - "auditd.data.dport", - "auditd.data.new-seuser", - "auditd.data.invalid_context", - "auditd.data.dmac", - "auditd.data.ipx-net", - "auditd.data.iuid", - "auditd.data.macproto", - "auditd.data.obj", - "auditd.data.ipid", - "auditd.data.new-fs", - "auditd.data.vm-pid", - "auditd.data.cap_pi", - "auditd.data.old-auid", - "auditd.data.oses", - "auditd.data.fd", - "auditd.data.igid", - "auditd.data.new-disk", - "auditd.data.parent", - "auditd.data.len", - "auditd.data.oflag", - "auditd.data.uuid", - "auditd.data.code", - "auditd.data.nlnk-grp", - "auditd.data.cap_fp", - "auditd.data.new-mem", - "auditd.data.seperm", - "auditd.data.enforcing", - "auditd.data.new-chardev", - "auditd.data.old-rng", - "auditd.data.outif", - "auditd.data.cmd", - "auditd.data.hook", - "auditd.data.new-level", - "auditd.data.sauid", - "auditd.data.sig", - "auditd.data.audit_backlog_wait_time", - "auditd.data.printer", - "auditd.data.old-mem", - "auditd.data.perm", - "auditd.data.old_pi", - "auditd.data.state", - "auditd.data.format", - "auditd.data.new_gid", - "auditd.data.tcontext", - "auditd.data.maj", - "auditd.data.watch", - "auditd.data.device", - "auditd.data.grp", - "auditd.data.bool", - "auditd.data.icmp_type", - "auditd.data.new_lock", - "auditd.data.old_prom", - "auditd.data.acl", - "auditd.data.ip", - "auditd.data.new_pi", - "auditd.data.default-context", - "auditd.data.inode_gid", - "auditd.data.new-log_passwd", - "auditd.data.new_pe", - "auditd.data.selected-context", - "auditd.data.cap_fver", - "auditd.data.file", - "auditd.data.net", - "auditd.data.virt", - "auditd.data.cap_pp", - "auditd.data.old-range", - "auditd.data.resrc", - "auditd.data.new-range", - "auditd.data.obj_gid", - "auditd.data.proto", - "auditd.data.old-disk", - "auditd.data.audit_failure", - "auditd.data.inif", - "auditd.data.vm", - "auditd.data.flags", - "auditd.data.nlnk-fam", - "auditd.data.old-fs", - "auditd.data.old-ses", - "auditd.data.seqno", - "auditd.data.fver", - "auditd.data.qbytes", - "auditd.data.seuser", - "auditd.data.cap_fe", - "auditd.data.new-vcpu", - "auditd.data.old-level", - "auditd.data.old_pp", - "auditd.data.daddr", - "auditd.data.old-role", - "auditd.data.ioctlcmd", - "auditd.data.smac", - "auditd.data.apparmor", - "auditd.data.fe", - "auditd.data.perm_mask", - "auditd.data.ses", - "auditd.data.cap_fi", - "auditd.data.obj_uid", - "auditd.data.reason", - "auditd.data.list", - "auditd.data.old_lock", - "auditd.data.bus", - "auditd.data.old_pe", - "auditd.data.new-role", - "auditd.data.prom", - "auditd.data.uri", - "auditd.data.audit_enabled", - "auditd.data.old-log_passwd", - "auditd.data.old-seuser", - "auditd.data.per", - "auditd.data.scontext", - "auditd.data.tclass", - "auditd.data.ver", - "auditd.data.new", - "auditd.data.val", - "auditd.data.img-ctx", - "auditd.data.old-chardev", - "auditd.data.old_val", - "auditd.data.success", - "auditd.data.inode_uid", - "auditd.data.removed", - "auditd.data.socket.port", - "auditd.data.socket.saddr", - "auditd.data.socket.addr", - "auditd.data.socket.family", - "auditd.data.socket.path", - "geoip.continent_name", - "geoip.city_name", - "geoip.region_name", - "geoip.country_iso_code", - "hash.blake2b_256", - "hash.blake2b_384", - "hash.blake2b_512", - "hash.md5", - "hash.sha1", - "hash.sha224", - "hash.sha256", - "hash.sha384", - "hash.sha3_224", - "hash.sha3_256", - "hash.sha3_384", - "hash.sha3_512", - "hash.sha512", - "hash.sha512_224", - "hash.sha512_256", - "hash.xxh64", - "event.origin", - "user.entity_id", - "user.terminal", - "process.entity_id", - "process.hash.blake2b_256", - "process.hash.blake2b_384", - "process.hash.blake2b_512", - "process.hash.sha224", - "process.hash.sha384", - "process.hash.sha3_224", - "process.hash.sha3_256", - "process.hash.sha3_384", - "process.hash.sha3_512", - "process.hash.sha512_224", - "process.hash.sha512_256", - "process.hash.xxh64", - "socket.entity_id", - "system.audit.host.timezone.name", - "system.audit.host.hostname", - "system.audit.host.id", - "system.audit.host.architecture", - "system.audit.host.mac", - "system.audit.host.os.codename", - "system.audit.host.os.platform", - "system.audit.host.os.name", - "system.audit.host.os.family", - "system.audit.host.os.version", - "system.audit.host.os.kernel", - "system.audit.package.entity_id", - "system.audit.package.name", - "system.audit.package.version", - "system.audit.package.release", - "system.audit.package.arch", - "system.audit.package.license", - "system.audit.package.summary", - "system.audit.package.url", - "system.audit.user.name", - "system.audit.user.uid", - "system.audit.user.gid", - "system.audit.user.dir", - "system.audit.user.shell", - "system.audit.user.user_information", - "system.audit.user.password.type", - "fields.*" - ] - }, - "refresh_interval": "5s" - } - } - } -} diff --git a/x-pack/test/security_solution_cypress/es_archives/case_and_timeline/data.json.gz b/x-pack/test/security_solution_cypress/es_archives/case_and_timeline/data.json.gz deleted file mode 100644 index 5838d18e1c7dd..0000000000000 Binary files a/x-pack/test/security_solution_cypress/es_archives/case_and_timeline/data.json.gz and /dev/null differ diff --git a/x-pack/test/security_solution_cypress/es_archives/case_and_timeline/mappings.json b/x-pack/test/security_solution_cypress/es_archives/case_and_timeline/mappings.json deleted file mode 100644 index 557bf347b64a4..0000000000000 --- a/x-pack/test/security_solution_cypress/es_archives/case_and_timeline/mappings.json +++ /dev/null @@ -1,2616 +0,0 @@ -{ - "type": "index", - "value": { - "aliases": { - ".kibana": { - } - }, - "index": ".kibana_1", - "mappings": { - "_meta": { - "migrationMappingPropertyHashes": { - "action": "6e96ac5e648f57523879661ea72525b7", - "action_task_params": "a9d49f184ee89641044be0ca2950fa3a", - "alert": "7b44fba6773e37c806ce290ea9b7024e", - "apm-indices": "9bb9b2bf1fa636ed8619cbab5ce6a1dd", - "apm-telemetry": "3d1b76c39bfb2cc8296b024d73854724", - "app_search_telemetry": "3d1b76c39bfb2cc8296b024d73854724", - "application_usage_totals": "3d1b76c39bfb2cc8296b024d73854724", - "application_usage_transactional": "43b8830d5d0df85a6823d290885fc9fd", - "canvas-element": "7390014e1091044523666d97247392fc", - "canvas-workpad": "b0a1706d356228dbdcb4a17e6b9eb231", - "canvas-workpad-template": "ae2673f678281e2c055d764b153e9715", - "cases": "32aa96a6d3855ddda53010ae2048ac22", - "cases-comments": "c2061fb929f585df57425102fa928b4b", - "cases-configure": "42711cbb311976c0687853f4c1354572", - "cases-user-actions": "32277330ec6b721abe3b846cfd939a71", - "config": "c63748b75f39d0c54de12d12c1ccbc20", - "dashboard": "74eb4b909f81222fa1ddeaba2881a37e", - "endpoint:user-artifact": "4a11183eee21e6fbad864f7a30b39ad0", - "endpoint:user-artifact-manifest": "4b9c0e7cfaf86d82a7ee9ed68065e50d", - "enterprise_search_telemetry": "3d1b76c39bfb2cc8296b024d73854724", - "epm-packages": "386dc9996a3b74607de64c2ab2171582", - "exception-list": "497afa2f881a675d72d58e20057f3d8b", - "exception-list-agnostic": "497afa2f881a675d72d58e20057f3d8b", - "file-upload-telemetry": "0ed4d3e1983d1217a30982630897092e", - "fleet-agent-actions": "9511b565b1cc6441a42033db3d5de8e9", - "fleet-agent-events": "e20a508b6e805189356be381dbfac8db", - "fleet-agents": "6012d61d15e72564e47fc3402332756e", - "fleet-enrollment-api-keys": "a69ef7ae661dab31561d6c6f052ef2a7", - "graph-workspace": "cd7ba1330e6682e9cc00b78850874be1", - "index-pattern": "45915a1ad866812242df474eb0479052", - "infrastructure-ui-source": "2b2809653635caf490c93f090502d04c", - "ingest-agent-policies": "8b0733cce189659593659dad8db426f0", - "ingest-outputs": "8aa988c376e65443fefc26f1075e93a3", - "ingest-package-policies": "f74dfe498e1849267cda41580b2be110", - "ingest_manager_settings": "02a03095f0e05b7a538fa801b88a217f", - "inventory-view": "88fc7e12fd1b45b6f0787323ce4f18d2", - "kql-telemetry": "d12a98a6f19a2d273696597547e064ee", - "lens": "52346cfec69ff7b47d5f0c12361a2797", - "lens-ui-telemetry": "509bfa5978586998e05f9e303c07a327", - "map": "4a05b35c3a3a58fbc72dd0202dc3487f", - "maps-telemetry": "5ef305b18111b77789afefbd36b66171", - "metrics-explorer-view": "a8df1d270ee48c969d22d23812d08187", - "migrationVersion": "4a1746014a75ade3a714e1db5763276f", - "ml-telemetry": "257fd1d4b4fdbb9cb4b8a3b27da201e9", - "namespace": "2f4316de49999235636386fe51dc06c1", - "namespaces": "2f4316de49999235636386fe51dc06c1", - "originId": "2f4316de49999235636386fe51dc06c1", - "query": "11aaeb7f5f7fa5bb43f25e18ce26e7d9", - "references": "7997cf5a56cc02bdc9c93361bde732b0", - "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", - "search": "7f9e077078cab612f6a58e3bfdedb71a", - "search-telemetry": "3d1b76c39bfb2cc8296b024d73854724", - "siem-detection-engine-rule-actions": "6569b288c169539db10cb262bf79de18", - "siem-detection-engine-rule-status": "ae783f41c6937db6b7a2ef5c93a9e9b0", - "siem-ui-timeline": "94bc38c7a421d15fbfe8ea565370a421", - "siem-ui-timeline-note": "8874706eedc49059d4cf0f5094559084", - "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", - "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", - "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", - "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", - "type": "2f4316de49999235636386fe51dc06c1", - "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", - "updated_at": "00da57df13e94e9d98437d13ace4bfe0", - "upgrade-assistant-reindex-operation": "215107c281839ea9b3ad5f6419819763", - "upgrade-assistant-telemetry": "56702cec857e0a9dacfb696655b4ff7b", - "uptime-dynamic-settings": "3d1b76c39bfb2cc8296b024d73854724", - "url": "c7f66a0df8b1b52f17c28c4adb111105", - "visualization": "44d6bd48a1a653bcb60ea01614b9e3c9", - "workplace_search_telemetry": "3d1b76c39bfb2cc8296b024d73854724" - } - }, - "dynamic": "strict", - "properties": { - "action": { - "properties": { - "actionTypeId": { - "type": "keyword" - }, - "config": { - "enabled": false, - "type": "object" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - }, - "secrets": { - "type": "binary" - } - } - }, - "action_task_params": { - "properties": { - "actionId": { - "type": "keyword" - }, - "apiKey": { - "type": "binary" - }, - "params": { - "enabled": false, - "type": "object" - } - } - }, - "agent_actions": { - "dynamic": "false", - "type": "object" - }, - "agent_configs": { - "dynamic": "false", - "type": "object" - }, - "agent_events": { - "dynamic": "false", - "type": "object" - }, - "agents": { - "dynamic": "false", - "type": "object" - }, - "alert": { - "properties": { - "actions": { - "properties": { - "actionRef": { - "type": "keyword" - }, - "actionTypeId": { - "type": "keyword" - }, - "group": { - "type": "keyword" - }, - "params": { - "enabled": false, - "type": "object" - } - }, - "type": "nested" - }, - "alertTypeId": { - "type": "keyword" - }, - "apiKey": { - "type": "binary" - }, - "apiKeyOwner": { - "type": "keyword" - }, - "consumer": { - "type": "keyword" - }, - "createdAt": { - "type": "date" - }, - "createdBy": { - "type": "keyword" - }, - "enabled": { - "type": "boolean" - }, - "muteAll": { - "type": "boolean" - }, - "mutedInstanceIds": { - "type": "keyword" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - }, - "params": { - "enabled": false, - "type": "object" - }, - "schedule": { - "properties": { - "interval": { - "type": "keyword" - } - } - }, - "scheduledTaskId": { - "type": "keyword" - }, - "tags": { - "type": "keyword" - }, - "throttle": { - "type": "keyword" - }, - "updatedBy": { - "type": "keyword" - } - } - }, - "apm-indices": { - "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } - } - } - }, - "apm-telemetry": { - "dynamic": "false", - "type": "object" - }, - "app_search_telemetry": { - "dynamic": "false", - "type": "object" - }, - "application_usage_totals": { - "dynamic": "false", - "type": "object" - }, - "application_usage_transactional": { - "dynamic": "false", - "properties": { - "timestamp": { - "type": "date" - } - } - }, - "canvas-element": { - "dynamic": "false", - "properties": { - "@created": { - "type": "date" - }, - "@timestamp": { - "type": "date" - }, - "content": { - "type": "text" - }, - "help": { - "type": "text" - }, - "image": { - "type": "text" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "canvas-workpad": { - "dynamic": "false", - "properties": { - "@created": { - "type": "date" - }, - "@timestamp": { - "type": "date" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "canvas-workpad-template": { - "dynamic": "false", - "properties": { - "help": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - }, - "tags": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - }, - "template_key": { - "type": "keyword" - } - } - }, - "cases": { - "properties": { - "closed_at": { - "type": "date" - }, - "closed_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "connector_id": { - "type": "keyword" - }, - "created_at": { - "type": "date" - }, - "created_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "description": { - "type": "text" - }, - "external_service": { - "properties": { - "connector_id": { - "type": "keyword" - }, - "connector_name": { - "type": "keyword" - }, - "external_id": { - "type": "keyword" - }, - "external_title": { - "type": "text" - }, - "external_url": { - "type": "text" - }, - "pushed_at": { - "type": "date" - }, - "pushed_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - } - } - }, - "status": { - "type": "keyword" - }, - "tags": { - "type": "keyword" - }, - "title": { - "type": "keyword" - }, - "updated_at": { - "type": "date" - }, - "updated_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - } - } - }, - "cases-comments": { - "properties": { - "comment": { - "type": "text" - }, - "created_at": { - "type": "date" - }, - "created_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "pushed_at": { - "type": "date" - }, - "pushed_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "updated_at": { - "type": "date" - }, - "updated_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - } - } - }, - "cases-configure": { - "properties": { - "closure_type": { - "type": "keyword" - }, - "connector_id": { - "type": "keyword" - }, - "connector_name": { - "type": "keyword" - }, - "created_at": { - "type": "date" - }, - "created_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "updated_at": { - "type": "date" - }, - "updated_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - } - } - }, - "cases-user-actions": { - "properties": { - "action": { - "type": "keyword" - }, - "action_at": { - "type": "date" - }, - "action_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "action_field": { - "type": "keyword" - }, - "new_value": { - "type": "text" - }, - "old_value": { - "type": "text" - } - } - }, - "config": { - "dynamic": "false", - "properties": { - "buildNum": { - "type": "keyword" - } - } - }, - "dashboard": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "doc_values": false, - "index": false, - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "index": false, - "type": "text" - } - } - }, - "optionsJSON": { - "index": false, - "type": "text" - }, - "panelsJSON": { - "index": false, - "type": "text" - }, - "refreshInterval": { - "properties": { - "display": { - "doc_values": false, - "index": false, - "type": "keyword" - }, - "pause": { - "doc_values": false, - "index": false, - "type": "boolean" - }, - "section": { - "doc_values": false, - "index": false, - "type": "integer" - }, - "value": { - "doc_values": false, - "index": false, - "type": "integer" - } - } - }, - "timeFrom": { - "doc_values": false, - "index": false, - "type": "keyword" - }, - "timeRestore": { - "doc_values": false, - "index": false, - "type": "boolean" - }, - "timeTo": { - "doc_values": false, - "index": false, - "type": "keyword" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "datasources": { - "dynamic": "false", - "type": "object" - }, - "endpoint:user-artifact": { - "properties": { - "body": { - "type": "binary" - }, - "compressionAlgorithm": { - "index": false, - "type": "keyword" - }, - "created": { - "index": false, - "type": "date" - }, - "decodedSha256": { - "index": false, - "type": "keyword" - }, - "decodedSize": { - "index": false, - "type": "long" - }, - "encodedSha256": { - "type": "keyword" - }, - "encodedSize": { - "index": false, - "type": "long" - }, - "encryptionAlgorithm": { - "index": false, - "type": "keyword" - }, - "identifier": { - "type": "keyword" - } - } - }, - "endpoint:user-artifact-manifest": { - "properties": { - "created": { - "index": false, - "type": "date" - }, - "ids": { - "index": false, - "type": "keyword" - }, - "schemaVersion": { - "type": "keyword" - }, - "semanticVersion": { - "index": false, - "type": "keyword" - } - } - }, - "enrollment_api_keys": { - "dynamic": "false", - "type": "object" - }, - "enterprise_search_telemetry": { - "dynamic": "false", - "type": "object" - }, - "epm-package": { - "dynamic": "false", - "type": "object" - }, - "epm-packages": { - "properties": { - "es_index_patterns": { - "enabled": false, - "type": "object" - }, - "install_started_at": { - "type": "date" - }, - "install_status": { - "type": "keyword" - }, - "install_version": { - "type": "keyword" - }, - "installed_es": { - "properties": { - "id": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - }, - "type": "nested" - }, - "installed_kibana": { - "properties": { - "id": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - }, - "type": "nested" - }, - "internal": { - "type": "boolean" - }, - "name": { - "type": "keyword" - }, - "removable": { - "type": "boolean" - }, - "version": { - "type": "keyword" - } - } - }, - "exception-list": { - "properties": { - "_tags": { - "type": "keyword" - }, - "comments": { - "properties": { - "comment": { - "type": "keyword" - }, - "created_at": { - "type": "keyword" - }, - "created_by": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "updated_at": { - "type": "keyword" - }, - "updated_by": { - "type": "keyword" - } - } - }, - "created_at": { - "type": "keyword" - }, - "created_by": { - "type": "keyword" - }, - "description": { - "type": "keyword" - }, - "entries": { - "properties": { - "entries": { - "properties": { - "field": { - "type": "keyword" - }, - "operator": { - "type": "keyword" - }, - "type": { - "type": "keyword" - }, - "value": { - "fields": { - "text": { - "type": "text" - } - }, - "type": "keyword" - } - } - }, - "field": { - "type": "keyword" - }, - "list": { - "properties": { - "id": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "operator": { - "type": "keyword" - }, - "type": { - "type": "keyword" - }, - "value": { - "fields": { - "text": { - "type": "text" - } - }, - "type": "keyword" - } - } - }, - "immutable": { - "type": "boolean" - }, - "item_id": { - "type": "keyword" - }, - "list_id": { - "type": "keyword" - }, - "list_type": { - "type": "keyword" - }, - "meta": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "tags": { - "type": "keyword" - }, - "tie_breaker_id": { - "type": "keyword" - }, - "type": { - "type": "keyword" - }, - "updated_by": { - "type": "keyword" - }, - "version": { - "type": "keyword" - } - } - }, - "exception-list-agnostic": { - "properties": { - "_tags": { - "type": "keyword" - }, - "comments": { - "properties": { - "comment": { - "type": "keyword" - }, - "created_at": { - "type": "keyword" - }, - "created_by": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "updated_at": { - "type": "keyword" - }, - "updated_by": { - "type": "keyword" - } - } - }, - "created_at": { - "type": "keyword" - }, - "created_by": { - "type": "keyword" - }, - "description": { - "type": "keyword" - }, - "entries": { - "properties": { - "entries": { - "properties": { - "field": { - "type": "keyword" - }, - "operator": { - "type": "keyword" - }, - "type": { - "type": "keyword" - }, - "value": { - "fields": { - "text": { - "type": "text" - } - }, - "type": "keyword" - } - } - }, - "field": { - "type": "keyword" - }, - "list": { - "properties": { - "id": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "operator": { - "type": "keyword" - }, - "type": { - "type": "keyword" - }, - "value": { - "fields": { - "text": { - "type": "text" - } - }, - "type": "keyword" - } - } - }, - "immutable": { - "type": "boolean" - }, - "item_id": { - "type": "keyword" - }, - "list_id": { - "type": "keyword" - }, - "list_type": { - "type": "keyword" - }, - "meta": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "tags": { - "type": "keyword" - }, - "tie_breaker_id": { - "type": "keyword" - }, - "type": { - "type": "keyword" - }, - "updated_by": { - "type": "keyword" - }, - "version": { - "type": "keyword" - } - } - }, - "file-upload-telemetry": { - "properties": { - "filesUploadedTotalCount": { - "type": "long" - } - } - }, - "fleet-agent-actions": { - "properties": { - "ack_data": { - "type": "text" - }, - "agent_id": { - "type": "keyword" - }, - "created_at": { - "type": "date" - }, - "data": { - "type": "binary" - }, - "policy_id": { - "type": "keyword" - }, - "policy_revision": { - "type": "integer" - }, - "sent_at": { - "type": "date" - }, - "type": { - "type": "keyword" - } - } - }, - "fleet-agent-events": { - "properties": { - "action_id": { - "type": "keyword" - }, - "agent_id": { - "type": "keyword" - }, - "data": { - "type": "text" - }, - "message": { - "type": "text" - }, - "payload": { - "type": "text" - }, - "policy_id": { - "type": "keyword" - }, - "stream_id": { - "type": "keyword" - }, - "subtype": { - "type": "keyword" - }, - "timestamp": { - "type": "date" - }, - "type": { - "type": "keyword" - } - } - }, - "fleet-agents": { - "properties": { - "access_api_key_id": { - "type": "keyword" - }, - "active": { - "type": "boolean" - }, - "current_error_events": { - "index": false, - "type": "text" - }, - "default_api_key": { - "type": "binary" - }, - "default_api_key_id": { - "type": "keyword" - }, - "enrolled_at": { - "type": "date" - }, - "last_checkin": { - "type": "date" - }, - "last_checkin_status": { - "type": "keyword" - }, - "last_updated": { - "type": "date" - }, - "local_metadata": { - "type": "flattened" - }, - "packages": { - "type": "keyword" - }, - "policy_id": { - "type": "keyword" - }, - "policy_revision": { - "type": "integer" - }, - "shared_id": { - "type": "keyword" - }, - "type": { - "type": "keyword" - }, - "unenrolled_at": { - "type": "date" - }, - "unenrollment_started_at": { - "type": "date" - }, - "updated_at": { - "type": "date" - }, - "user_provided_metadata": { - "type": "flattened" - }, - "version": { - "type": "keyword" - } - } - }, - "fleet-enrollment-api-keys": { - "properties": { - "active": { - "type": "boolean" - }, - "api_key": { - "type": "binary" - }, - "api_key_id": { - "type": "keyword" - }, - "created_at": { - "type": "date" - }, - "expire_at": { - "type": "date" - }, - "name": { - "type": "keyword" - }, - "policy_id": { - "type": "keyword" - }, - "type": { - "type": "keyword" - }, - "updated_at": { - "type": "date" - } - } - }, - "graph-workspace": { - "properties": { - "description": { - "type": "text" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "numLinks": { - "type": "integer" - }, - "numVertices": { - "type": "integer" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - }, - "wsState": { - "type": "text" - } - } - }, - "index-pattern": { - "dynamic": "false", - "properties": { - "title": { - "type": "text" - }, - "type": { - "type": "keyword" - } - } - }, - "infrastructure-ui-source": { - "properties": { - "description": { - "type": "text" - }, - "fields": { - "properties": { - "container": { - "type": "keyword" - }, - "host": { - "type": "keyword" - }, - "pod": { - "type": "keyword" - }, - "tiebreaker": { - "type": "keyword" - }, - "timestamp": { - "type": "keyword" - } - } - }, - "inventoryDefaultView": { - "type": "keyword" - }, - "logAlias": { - "type": "keyword" - }, - "logColumns": { - "properties": { - "fieldColumn": { - "properties": { - "field": { - "type": "keyword" - }, - "id": { - "type": "keyword" - } - } - }, - "messageColumn": { - "properties": { - "id": { - "type": "keyword" - } - } - }, - "timestampColumn": { - "properties": { - "id": { - "type": "keyword" - } - } - } - }, - "type": "nested" - }, - "metricAlias": { - "type": "keyword" - }, - "metricsExplorerDefaultView": { - "type": "keyword" - }, - "name": { - "type": "text" - } - } - }, - "ingest-agent-policies": { - "properties": { - "description": { - "type": "text" - }, - "is_default": { - "type": "boolean" - }, - "monitoring_enabled": { - "index": false, - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "namespace": { - "type": "keyword" - }, - "package_policies": { - "type": "keyword" - }, - "revision": { - "type": "integer" - }, - "status": { - "type": "keyword" - }, - "updated_at": { - "type": "date" - }, - "updated_by": { - "type": "keyword" - } - } - }, - "ingest-outputs": { - "properties": { - "ca_sha256": { - "index": false, - "type": "keyword" - }, - "config": { - "type": "flattened" - }, - "fleet_enroll_password": { - "type": "binary" - }, - "fleet_enroll_username": { - "type": "binary" - }, - "hosts": { - "type": "keyword" - }, - "is_default": { - "type": "boolean" - }, - "name": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "ingest-package-policies": { - "properties": { - "created_at": { - "type": "date" - }, - "created_by": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "enabled": { - "type": "boolean" - }, - "inputs": { - "enabled": false, - "properties": { - "config": { - "type": "flattened" - }, - "enabled": { - "type": "boolean" - }, - "streams": { - "properties": { - "compiled_stream": { - "type": "flattened" - }, - "config": { - "type": "flattened" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "enabled": { - "type": "boolean" - }, - "id": { - "type": "keyword" - }, - "vars": { - "type": "flattened" - } - }, - "type": "nested" - }, - "type": { - "type": "keyword" - }, - "vars": { - "type": "flattened" - } - }, - "type": "nested" - }, - "name": { - "type": "keyword" - }, - "namespace": { - "type": "keyword" - }, - "output_id": { - "type": "keyword" - }, - "package": { - "properties": { - "name": { - "type": "keyword" - }, - "title": { - "type": "keyword" - }, - "version": { - "type": "keyword" - } - } - }, - "policy_id": { - "type": "keyword" - }, - "revision": { - "type": "integer" - }, - "updated_at": { - "type": "date" - }, - "updated_by": { - "type": "keyword" - } - } - }, - "ingest_manager_settings": { - "properties": { - "agent_auto_upgrade": { - "type": "keyword" - }, - "has_seen_add_data_notice": { - "index": false, - "type": "boolean" - }, - "kibana_ca_sha256": { - "type": "keyword" - }, - "kibana_urls": { - "type": "keyword" - }, - "package_auto_upgrade": { - "type": "keyword" - } - } - }, - "inventory-view": { - "properties": { - "accountId": { - "type": "keyword" - }, - "autoBounds": { - "type": "boolean" - }, - "autoReload": { - "type": "boolean" - }, - "boundsOverride": { - "properties": { - "max": { - "type": "integer" - }, - "min": { - "type": "integer" - } - } - }, - "customMetrics": { - "properties": { - "aggregation": { - "type": "keyword" - }, - "field": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "label": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - }, - "type": "nested" - }, - "customOptions": { - "properties": { - "field": { - "type": "keyword" - }, - "text": { - "type": "keyword" - } - }, - "type": "nested" - }, - "filterQuery": { - "properties": { - "expression": { - "type": "keyword" - }, - "kind": { - "type": "keyword" - } - } - }, - "groupBy": { - "properties": { - "field": { - "type": "keyword" - }, - "label": { - "type": "keyword" - } - }, - "type": "nested" - }, - "legend": { - "properties": { - "palette": { - "type": "keyword" - }, - "reverseColors": { - "type": "boolean" - }, - "steps": { - "type": "long" - } - } - }, - "metric": { - "properties": { - "aggregation": { - "type": "keyword" - }, - "field": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "label": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "name": { - "type": "keyword" - }, - "nodeType": { - "type": "keyword" - }, - "region": { - "type": "keyword" - }, - "sort": { - "properties": { - "by": { - "type": "keyword" - }, - "direction": { - "type": "keyword" - } - } - }, - "time": { - "type": "long" - }, - "view": { - "type": "keyword" - } - } - }, - "kql-telemetry": { - "properties": { - "optInCount": { - "type": "long" - }, - "optOutCount": { - "type": "long" - } - } - }, - "lens": { - "properties": { - "description": { - "type": "text" - }, - "expression": { - "doc_values": false, - "index": false, - "type": "keyword" - }, - "state": { - "type": "flattened" - }, - "title": { - "type": "text" - }, - "visualizationType": { - "type": "keyword" - } - } - }, - "lens-ui-telemetry": { - "properties": { - "count": { - "type": "integer" - }, - "date": { - "type": "date" - }, - "name": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "map": { - "properties": { - "description": { - "type": "text" - }, - "layerListJSON": { - "type": "text" - }, - "mapStateJSON": { - "type": "text" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "maps-telemetry": { - "enabled": false, - "type": "object" - }, - "metrics-explorer-view": { - "properties": { - "chartOptions": { - "properties": { - "stack": { - "type": "boolean" - }, - "type": { - "type": "keyword" - }, - "yAxisMode": { - "type": "keyword" - } - } - }, - "currentTimerange": { - "properties": { - "from": { - "type": "keyword" - }, - "interval": { - "type": "keyword" - }, - "to": { - "type": "keyword" - } - } - }, - "name": { - "type": "keyword" - }, - "options": { - "properties": { - "aggregation": { - "type": "keyword" - }, - "filterQuery": { - "type": "keyword" - }, - "forceInterval": { - "type": "boolean" - }, - "groupBy": { - "type": "keyword" - }, - "limit": { - "type": "integer" - }, - "metrics": { - "properties": { - "aggregation": { - "type": "keyword" - }, - "color": { - "type": "keyword" - }, - "field": { - "type": "keyword" - }, - "label": { - "type": "keyword" - } - }, - "type": "nested" - }, - "source": { - "type": "keyword" - } - } - } - } - }, - "migrationVersion": { - "dynamic": "true", - "properties": { - "config": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, - "space": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "ml-telemetry": { - "properties": { - "file_data_visualizer": { - "properties": { - "index_creation_count": { - "type": "long" - } - } - } - } - }, - "namespace": { - "type": "keyword" - }, - "namespaces": { - "type": "keyword" - }, - "originId": { - "type": "keyword" - }, - "outputs": { - "dynamic": "false", - "type": "object" - }, - "query": { - "properties": { - "description": { - "type": "text" - }, - "filters": { - "enabled": false, - "type": "object" - }, - "query": { - "properties": { - "language": { - "type": "keyword" - }, - "query": { - "index": false, - "type": "keyword" - } - } - }, - "timefilter": { - "enabled": false, - "type": "object" - }, - "title": { - "type": "text" - } - } - }, - "references": { - "properties": { - "id": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - }, - "type": "nested" - }, - "sample-data-telemetry": { - "properties": { - "installCount": { - "type": "long" - }, - "unInstallCount": { - "type": "long" - } - } - }, - "search": { - "properties": { - "columns": { - "doc_values": false, - "index": false, - "type": "keyword" - }, - "description": { - "type": "text" - }, - "hits": { - "doc_values": false, - "index": false, - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "index": false, - "type": "text" - } - } - }, - "sort": { - "doc_values": false, - "index": false, - "type": "keyword" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "search-telemetry": { - "dynamic": "false", - "type": "object" - }, - "server": { - "dynamic": "false", - "type": "object" - }, - "siem-detection-engine-rule-actions": { - "properties": { - "actions": { - "properties": { - "action_type_id": { - "type": "keyword" - }, - "group": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "params": { - "enabled": false, - "type": "object" - } - } - }, - "alertThrottle": { - "type": "keyword" - }, - "ruleAlertId": { - "type": "keyword" - }, - "ruleThrottle": { - "type": "keyword" - } - } - }, - "siem-detection-engine-rule-status": { - "properties": { - "alertId": { - "type": "keyword" - }, - "bulkCreateTimeDurations": { - "type": "float" - }, - "gap": { - "type": "text" - }, - "lastFailureAt": { - "type": "date" - }, - "lastFailureMessage": { - "type": "text" - }, - "lastLookBackDate": { - "type": "date" - }, - "lastSuccessAt": { - "type": "date" - }, - "lastSuccessMessage": { - "type": "text" - }, - "searchAfterTimeDurations": { - "type": "float" - }, - "status": { - "type": "keyword" - }, - "statusDate": { - "type": "date" - } - } - }, - "siem-ui-timeline": { - "properties": { - "columns": { - "properties": { - "aggregatable": { - "type": "boolean" - }, - "category": { - "type": "keyword" - }, - "columnHeaderType": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "example": { - "type": "text" - }, - "id": { - "type": "keyword" - }, - "indexes": { - "type": "keyword" - }, - "name": { - "type": "text" - }, - "placeholder": { - "type": "text" - }, - "searchable": { - "type": "boolean" - }, - "type": { - "type": "keyword" - } - } - }, - "created": { - "type": "date" - }, - "createdBy": { - "type": "text" - }, - "dataProviders": { - "properties": { - "and": { - "properties": { - "enabled": { - "type": "boolean" - }, - "excluded": { - "type": "boolean" - }, - "id": { - "type": "keyword" - }, - "kqlQuery": { - "type": "text" - }, - "name": { - "type": "text" - }, - "queryMatch": { - "properties": { - "displayField": { - "type": "text" - }, - "displayValue": { - "type": "text" - }, - "field": { - "type": "text" - }, - "operator": { - "type": "text" - }, - "value": { - "type": "text" - } - } - }, - "type": { - "type": "text" - } - } - }, - "enabled": { - "type": "boolean" - }, - "excluded": { - "type": "boolean" - }, - "id": { - "type": "keyword" - }, - "kqlQuery": { - "type": "text" - }, - "name": { - "type": "text" - }, - "queryMatch": { - "properties": { - "displayField": { - "type": "text" - }, - "displayValue": { - "type": "text" - }, - "field": { - "type": "text" - }, - "operator": { - "type": "text" - }, - "value": { - "type": "text" - } - } - }, - "type": { - "type": "text" - } - } - }, - "dateRange": { - "properties": { - "end": { - "type": "date" - }, - "start": { - "type": "date" - } - } - }, - "description": { - "type": "text" - }, - "eventType": { - "type": "keyword" - }, - "excludedRowRendererIds": { - "type": "text" - }, - "favorite": { - "properties": { - "favoriteDate": { - "type": "date" - }, - "fullName": { - "type": "text" - }, - "keySearch": { - "type": "text" - }, - "userName": { - "type": "text" - } - } - }, - "filters": { - "properties": { - "exists": { - "type": "text" - }, - "match_all": { - "type": "text" - }, - "meta": { - "properties": { - "alias": { - "type": "text" - }, - "controlledBy": { - "type": "text" - }, - "disabled": { - "type": "boolean" - }, - "field": { - "type": "text" - }, - "formattedValue": { - "type": "text" - }, - "index": { - "type": "keyword" - }, - "key": { - "type": "keyword" - }, - "negate": { - "type": "boolean" - }, - "params": { - "type": "text" - }, - "type": { - "type": "keyword" - }, - "value": { - "type": "text" - } - } - }, - "missing": { - "type": "text" - }, - "query": { - "type": "text" - }, - "range": { - "type": "text" - }, - "script": { - "type": "text" - } - } - }, - "kqlMode": { - "type": "keyword" - }, - "kqlQuery": { - "properties": { - "filterQuery": { - "properties": { - "kuery": { - "properties": { - "expression": { - "type": "text" - }, - "kind": { - "type": "keyword" - } - } - }, - "serializedQuery": { - "type": "text" - } - } - } - } - }, - "savedQueryId": { - "type": "keyword" - }, - "sort": { - "properties": { - "columnId": { - "type": "keyword" - }, - "sortDirection": { - "type": "keyword" - } - } - }, - "status": { - "type": "keyword" - }, - "templateTimelineId": { - "type": "text" - }, - "templateTimelineVersion": { - "type": "integer" - }, - "timelineType": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "updated": { - "type": "date" - }, - "updatedBy": { - "type": "text" - } - } - }, - "siem-ui-timeline-note": { - "properties": { - "created": { - "type": "date" - }, - "createdBy": { - "type": "text" - }, - "eventId": { - "type": "keyword" - }, - "note": { - "type": "text" - }, - "timelineId": { - "type": "keyword" - }, - "updated": { - "type": "date" - }, - "updatedBy": { - "type": "text" - } - } - }, - "siem-ui-timeline-pinned-event": { - "properties": { - "created": { - "type": "date" - }, - "createdBy": { - "type": "text" - }, - "eventId": { - "type": "keyword" - }, - "timelineId": { - "type": "keyword" - }, - "updated": { - "type": "date" - }, - "updatedBy": { - "type": "text" - } - } - }, - "space": { - "properties": { - "_reserved": { - "type": "boolean" - }, - "color": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "disabledFeatures": { - "type": "keyword" - }, - "imageUrl": { - "index": false, - "type": "text" - }, - "initials": { - "type": "keyword" - }, - "name": { - "fields": { - "keyword": { - "ignore_above": 2048, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "spaceId": { - "type": "keyword" - }, - "telemetry": { - "properties": { - "allowChangingOptInStatus": { - "type": "boolean" - }, - "enabled": { - "type": "boolean" - }, - "lastReported": { - "type": "date" - }, - "lastVersionChecked": { - "type": "keyword" - }, - "reportFailureCount": { - "type": "integer" - }, - "reportFailureVersion": { - "type": "keyword" - }, - "sendUsageFrom": { - "type": "keyword" - }, - "userHasSeenNotice": { - "type": "boolean" - } - } - }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "tsvb-validation-telemetry": { - "properties": { - "failedRequests": { - "type": "long" - } - } - }, - "type": { - "type": "keyword" - }, - "ui-metric": { - "properties": { - "count": { - "type": "integer" - } - } - }, - "updated_at": { - "type": "date" - }, - "upgrade-assistant-reindex-operation": { - "properties": { - "errorMessage": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, - "indexName": { - "type": "keyword" - }, - "lastCompletedStep": { - "type": "long" - }, - "locked": { - "type": "date" - }, - "newIndexName": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, - "reindexOptions": { - "properties": { - "openAndClose": { - "type": "boolean" - }, - "queueSettings": { - "properties": { - "queuedAt": { - "type": "long" - }, - "startedAt": { - "type": "long" - } - } - } - } - }, - "reindexTaskId": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, - "reindexTaskPercComplete": { - "type": "float" - }, - "runningReindexCount": { - "type": "integer" - }, - "status": { - "type": "integer" - } - } - }, - "upgrade-assistant-telemetry": { - "properties": { - "features": { - "properties": { - "deprecation_logging": { - "properties": { - "enabled": { - "null_value": true, - "type": "boolean" - } - } - } - } - }, - "ui_open": { - "properties": { - "cluster": { - "null_value": 0, - "type": "long" - }, - "indices": { - "null_value": 0, - "type": "long" - }, - "overview": { - "null_value": 0, - "type": "long" - } - } - }, - "ui_reindex": { - "properties": { - "close": { - "null_value": 0, - "type": "long" - }, - "open": { - "null_value": 0, - "type": "long" - }, - "start": { - "null_value": 0, - "type": "long" - }, - "stop": { - "null_value": 0, - "type": "long" - } - } - } - } - }, - "uptime-dynamic-settings": { - "dynamic": "false", - "type": "object" - }, - "url": { - "properties": { - "accessCount": { - "type": "long" - }, - "accessDate": { - "type": "date" - }, - "createDate": { - "type": "date" - }, - "url": { - "fields": { - "keyword": { - "ignore_above": 2048, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "visualization": { - "properties": { - "description": { - "type": "text" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "index": false, - "type": "text" - } - } - }, - "savedSearchRefName": { - "doc_values": false, - "index": false, - "type": "keyword" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "index": false, - "type": "text" - }, - "version": { - "type": "integer" - }, - "visState": { - "index": false, - "type": "text" - } - } - }, - "workplace_search_telemetry": { - "dynamic": "false", - "type": "object" - } - } - }, - "settings": { - "index": { - "auto_expand_replicas": "0-1", - "number_of_replicas": "0", - "number_of_shards": "1" - } - } - } -} \ No newline at end of file diff --git a/x-pack/test/security_solution_cypress/es_archives/closed_alerts/data.json.gz b/x-pack/test/security_solution_cypress/es_archives/closed_alerts/data.json.gz deleted file mode 100644 index 117c829b31d6e..0000000000000 Binary files a/x-pack/test/security_solution_cypress/es_archives/closed_alerts/data.json.gz and /dev/null differ diff --git a/x-pack/test/security_solution_cypress/es_archives/closed_alerts/mappings.json b/x-pack/test/security_solution_cypress/es_archives/closed_alerts/mappings.json deleted file mode 100644 index 00a2f6fb8c8df..0000000000000 --- a/x-pack/test/security_solution_cypress/es_archives/closed_alerts/mappings.json +++ /dev/null @@ -1,8124 +0,0 @@ -{ - "type": "index", - "value": { - "aliases": { - ".siem-signals-default": { - "is_write_index": true - } - }, - "index": ".siem-signals-default-000001", - "mappings": { - "dynamic": "false", - "_meta": { - "version": 3 - }, - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "properties": { - "ephemeral_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "client": { - "properties": { - "address": { - "type": "keyword", - "ignore_above": 1024 - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "availability_zone": { - "type": "keyword", - "ignore_above": 1024 - }, - "instance": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "machine": { - "properties": { - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "provider": { - "type": "keyword", - "ignore_above": 1024 - }, - "region": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "container": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "image": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "tag": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "runtime": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "destination": { - "properties": { - "address": { - "type": "keyword", - "ignore_above": 1024 - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "dll": { - "properties": { - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024 - }, - "pe": { - "properties": { - "company": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "file_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "original_file_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "type": "keyword", - "ignore_above": 1024 - }, - "data": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "ttl": { - "type": "long" - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "header_flags": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "op_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "question": { - "properties": { - "class": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "subdomain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ecs": { - "properties": { - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "error": { - "properties": { - "code": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "message": { - "type": "text", - "norms": false - }, - "stack_trace": { - "type": "keyword", - "index": false, - "doc_values": false, - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "event": { - "properties": { - "action": { - "type": "keyword", - "ignore_above": 1024 - }, - "category": { - "type": "keyword", - "ignore_above": 1024 - }, - "code": { - "type": "keyword", - "ignore_above": 1024 - }, - "created": { - "type": "date" - }, - "dataset": { - "type": "keyword", - "ignore_above": 1024 - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "ingested": { - "type": "date" - }, - "kind": { - "type": "keyword", - "ignore_above": 1024 - }, - "module": { - "type": "keyword", - "ignore_above": 1024 - }, - "original": { - "type": "keyword", - "index": false, - "doc_values": false, - "ignore_above": 1024 - }, - "outcome": { - "type": "keyword", - "ignore_above": 1024 - }, - "provider": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "url": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "type": "keyword", - "ignore_above": 1024 - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "type": "keyword", - "ignore_above": 1024 - }, - "directory": { - "type": "keyword", - "ignore_above": 1024 - }, - "drive_letter": { - "type": "keyword", - "ignore_above": 1 - }, - "extension": { - "type": "keyword", - "ignore_above": 1024 - }, - "gid": { - "type": "keyword", - "ignore_above": 1024 - }, - "group": { - "type": "keyword", - "ignore_above": 1024 - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "inode": { - "type": "keyword", - "ignore_above": 1024 - }, - "mime_type": { - "type": "keyword", - "ignore_above": 1024 - }, - "mode": { - "type": "keyword", - "ignore_above": 1024 - }, - "mtime": { - "type": "date" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "owner": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "pe": { - "properties": { - "company": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "file_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "original_file_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "size": { - "type": "long" - }, - "target_path": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "uid": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "host": { - "properties": { - "architecture": { - "type": "keyword", - "ignore_above": 1024 - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hostname": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "os": { - "properties": { - "family": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "kernel": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "platform": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "http": { - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "method": { - "type": "keyword", - "ignore_above": 1024 - }, - "referrer": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "interface": { - "properties": { - "alias": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "labels": { - "type": "object" - }, - "log": { - "properties": { - "level": { - "type": "keyword", - "ignore_above": 1024 - }, - "logger": { - "type": "keyword", - "ignore_above": 1024 - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "integer" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "function": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "original": { - "type": "keyword", - "index": false, - "doc_values": false, - "ignore_above": 1024 - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - } - } - }, - "message": { - "type": "text", - "norms": false - }, - "network": { - "properties": { - "application": { - "type": "keyword", - "ignore_above": 1024 - }, - "bytes": { - "type": "long" - }, - "community_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "direction": { - "type": "keyword", - "ignore_above": 1024 - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "type": "keyword", - "ignore_above": 1024 - }, - "inner": { - "properties": { - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "packets": { - "type": "long" - }, - "protocol": { - "type": "keyword", - "ignore_above": 1024 - }, - "transport": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "observer": { - "properties": { - "egress": { - "properties": { - "interface": { - "properties": { - "alias": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "zone": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hostname": { - "type": "keyword", - "ignore_above": 1024 - }, - "ingress": { - "properties": { - "interface": { - "properties": { - "alias": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "zone": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "os": { - "properties": { - "family": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "kernel": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "platform": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - }, - "serial_number": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "vendor": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "organization": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "os": { - "properties": { - "family": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "kernel": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "platform": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "package": { - "properties": { - "architecture": { - "type": "keyword", - "ignore_above": 1024 - }, - "build_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "checksum": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "install_scope": { - "type": "keyword", - "ignore_above": 1024 - }, - "installed": { - "type": "date" - }, - "license": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - }, - "size": { - "type": "long" - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "pe": { - "properties": { - "company": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "file_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "original_file_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "process": { - "properties": { - "args": { - "type": "keyword", - "ignore_above": 1024 - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "entity_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "executable": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "parent": { - "properties": { - "args": { - "type": "keyword", - "ignore_above": 1024 - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "entity_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "executable": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "title": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "pe": { - "properties": { - "company": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "file_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "original_file_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "title": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "type": "keyword", - "ignore_above": 1024 - }, - "strings": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hive": { - "type": "keyword", - "ignore_above": 1024 - }, - "key": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024 - }, - "value": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "related": { - "properties": { - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "ip": { - "type": "ip" - }, - "user": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "rule": { - "properties": { - "author": { - "type": "keyword", - "ignore_above": 1024 - }, - "category": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "license": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - }, - "ruleset": { - "type": "keyword", - "ignore_above": 1024 - }, - "uuid": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "server": { - "properties": { - "address": { - "type": "keyword", - "ignore_above": 1024 - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "service": { - "properties": { - "ephemeral_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "node": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "state": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "signal": { - "properties": { - "ancestors": { - "properties": { - "depth": { - "type": "long" - }, - "id": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "rule": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "depth": { - "type": "integer" - }, - "group": { - "properties": { - "id": { - "type": "keyword" - }, - "index": { - "type": "integer" - } - } - }, - "original_event": { - "properties": { - "action": { - "type": "keyword" - }, - "category": { - "type": "keyword" - }, - "code": { - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "kind": { - "type": "keyword" - }, - "module": { - "type": "keyword" - }, - "original": { - "type": "keyword", - "index": false, - "doc_values": false - }, - "outcome": { - "type": "keyword" - }, - "provider": { - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "original_signal": { - "type": "object", - "dynamic": "false", - "enabled": false - }, - "original_time": { - "type": "date" - }, - "parent": { - "properties": { - "depth": { - "type": "long" - }, - "id": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "rule": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "parents": { - "properties": { - "depth": { - "type": "long" - }, - "id": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "rule": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "author": { - "type": "keyword" - }, - "building_block_type": { - "type": "keyword" - }, - "created_at": { - "type": "date" - }, - "created_by": { - "type": "keyword" - }, - "description": { - "type": "keyword" - }, - "enabled": { - "type": "keyword" - }, - "false_positives": { - "type": "keyword" - }, - "filters": { - "type": "object" - }, - "from": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "immutable": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "interval": { - "type": "keyword" - }, - "language": { - "type": "keyword" - }, - "license": { - "type": "keyword" - }, - "max_signals": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "note": { - "type": "text" - }, - "output_index": { - "type": "keyword" - }, - "query": { - "type": "keyword" - }, - "references": { - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_mapping": { - "properties": { - "field": { - "type": "keyword" - }, - "operator": { - "type": "keyword" - }, - "value": { - "type": "keyword" - } - } - }, - "rule_id": { - "type": "keyword" - }, - "rule_name_override": { - "type": "keyword" - }, - "saved_id": { - "type": "keyword" - }, - "severity": { - "type": "keyword" - }, - "severity_mapping": { - "properties": { - "field": { - "type": "keyword" - }, - "operator": { - "type": "keyword" - }, - "severity": { - "type": "keyword" - }, - "value": { - "type": "keyword" - } - } - }, - "size": { - "type": "keyword" - }, - "tags": { - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "reference": { - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "reference": { - "type": "keyword" - } - } - } - } - }, - "threshold": { - "properties": { - "field": { - "type": "keyword" - }, - "value": { - "type": "float" - } - } - }, - "timeline_id": { - "type": "keyword" - }, - "timeline_title": { - "type": "keyword" - }, - "timestamp_override": { - "type": "keyword" - }, - "to": { - "type": "keyword" - }, - "type": { - "type": "keyword" - }, - "updated_at": { - "type": "date" - }, - "updated_by": { - "type": "keyword" - }, - "version": { - "type": "keyword" - } - } - }, - "status": { - "type": "keyword" - }, - "threshold_count": { - "type": "float" - }, - "threshold_result": { - "properties": { - "count": { - "type": "long" - }, - "value": { - "type": "keyword" - } - } - } - } - }, - "source": { - "properties": { - "address": { - "type": "keyword", - "ignore_above": 1024 - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "tags": { - "type": "keyword", - "ignore_above": 1024 - }, - "threat": { - "properties": { - "framework": { - "type": "keyword", - "ignore_above": 1024 - }, - "tactic": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "technique": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "tls": { - "properties": { - "cipher": { - "type": "keyword", - "ignore_above": 1024 - }, - "client": { - "properties": { - "certificate": { - "type": "keyword", - "ignore_above": 1024 - }, - "certificate_chain": { - "type": "keyword", - "ignore_above": 1024 - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "issuer": { - "type": "keyword", - "ignore_above": 1024 - }, - "ja3": { - "type": "keyword", - "ignore_above": 1024 - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject": { - "type": "keyword", - "ignore_above": 1024 - }, - "supported_ciphers": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "curve": { - "type": "keyword", - "ignore_above": 1024 - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "type": "keyword", - "ignore_above": 1024 - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "type": "keyword", - "ignore_above": 1024 - }, - "certificate_chain": { - "type": "keyword", - "ignore_above": 1024 - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "issuer": { - "type": "keyword", - "ignore_above": 1024 - }, - "ja3s": { - "type": "keyword", - "ignore_above": 1024 - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - }, - "version_protocol": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "trace": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "transaction": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "url": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "extension": { - "type": "keyword", - "ignore_above": 1024 - }, - "fragment": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "original": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "password": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024 - }, - "port": { - "type": "long" - }, - "query": { - "type": "keyword", - "ignore_above": 1024 - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "scheme": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "username": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "user_agent": { - "properties": { - "device": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "original": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "os": { - "properties": { - "family": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "kernel": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "platform": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "vulnerability": { - "properties": { - "category": { - "type": "keyword", - "ignore_above": 1024 - }, - "classification": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "enumeration": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - }, - "report_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "scanner": { - "properties": { - "vendor": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "severity": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "settings": { - "index": { - "lifecycle": { - "name": ".siem-signals-default", - "rollover_alias": ".siem-signals-default" - }, - "number_of_replicas": "1", - "number_of_shards": "1" - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - "auditbeat-7.6.0": { - "is_write_index": true - } - }, - "index": "auditbeat-7.6.0-2020.03.11-000001", - "mappings": { - "_meta": { - "beat": "auditbeat", - "version": "7.6.0" - }, - "date_detection": false, - "dynamic_templates": [ - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "container.labels.*" - } - }, - { - "dns.answers": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "dns.answers.*" - } - }, - { - "log.syslog": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "log.syslog.*" - } - }, - { - "fields": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "fields.*" - } - }, - { - "docker.container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "docker.container.labels.*" - } - }, - { - "kubernetes.labels.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.labels.*" - } - }, - { - "kubernetes.annotations.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.annotations.*" - } - }, - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "auditd": { - "properties": { - "data": { - "properties": { - "a0": { - "ignore_above": 1024, - "type": "keyword" - }, - "a1": { - "ignore_above": 1024, - "type": "keyword" - }, - "a2": { - "ignore_above": 1024, - "type": "keyword" - }, - "a3": { - "ignore_above": 1024, - "type": "keyword" - }, - "a[0-3]": { - "ignore_above": 1024, - "type": "keyword" - }, - "acct": { - "ignore_above": 1024, - "type": "keyword" - }, - "acl": { - "ignore_above": 1024, - "type": "keyword" - }, - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "added": { - "ignore_above": 1024, - "type": "keyword" - }, - "addr": { - "ignore_above": 1024, - "type": "keyword" - }, - "apparmor": { - "ignore_above": 1024, - "type": "keyword" - }, - "arch": { - "ignore_above": 1024, - "type": "keyword" - }, - "argc": { - "ignore_above": 1024, - "type": "keyword" - }, - "audit_backlog_limit": { - "ignore_above": 1024, - "type": "keyword" - }, - "audit_backlog_wait_time": { - "ignore_above": 1024, - "type": "keyword" - }, - "audit_enabled": { - "ignore_above": 1024, - "type": "keyword" - }, - "audit_failure": { - "ignore_above": 1024, - "type": "keyword" - }, - "banners": { - "ignore_above": 1024, - "type": "keyword" - }, - "bool": { - "ignore_above": 1024, - "type": "keyword" - }, - "bus": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_fe": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_fi": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_fp": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_fver": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_pe": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_pi": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_pp": { - "ignore_above": 1024, - "type": "keyword" - }, - "capability": { - "ignore_above": 1024, - "type": "keyword" - }, - "cgroup": { - "ignore_above": 1024, - "type": "keyword" - }, - "changed": { - "ignore_above": 1024, - "type": "keyword" - }, - "cipher": { - "ignore_above": 1024, - "type": "keyword" - }, - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "cmd": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "compat": { - "ignore_above": 1024, - "type": "keyword" - }, - "daddr": { - "ignore_above": 1024, - "type": "keyword" - }, - "data": { - "ignore_above": 1024, - "type": "keyword" - }, - "default-context": { - "ignore_above": 1024, - "type": "keyword" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "dir": { - "ignore_above": 1024, - "type": "keyword" - }, - "direction": { - "ignore_above": 1024, - "type": "keyword" - }, - "dmac": { - "ignore_above": 1024, - "type": "keyword" - }, - "dport": { - "ignore_above": 1024, - "type": "keyword" - }, - "enforcing": { - "ignore_above": 1024, - "type": "keyword" - }, - "entries": { - "ignore_above": 1024, - "type": "keyword" - }, - "exit": { - "ignore_above": 1024, - "type": "keyword" - }, - "fam": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "fd": { - "ignore_above": 1024, - "type": "keyword" - }, - "fe": { - "ignore_above": 1024, - "type": "keyword" - }, - "feature": { - "ignore_above": 1024, - "type": "keyword" - }, - "fi": { - "ignore_above": 1024, - "type": "keyword" - }, - "file": { - "ignore_above": 1024, - "type": "keyword" - }, - "flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "format": { - "ignore_above": 1024, - "type": "keyword" - }, - "fp": { - "ignore_above": 1024, - "type": "keyword" - }, - "fver": { - "ignore_above": 1024, - "type": "keyword" - }, - "grantors": { - "ignore_above": 1024, - "type": "keyword" - }, - "grp": { - "ignore_above": 1024, - "type": "keyword" - }, - "hook": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "icmp_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "igid": { - "ignore_above": 1024, - "type": "keyword" - }, - "img-ctx": { - "ignore_above": 1024, - "type": "keyword" - }, - "inif": { - "ignore_above": 1024, - "type": "keyword" - }, - "ino": { - "ignore_above": 1024, - "type": "keyword" - }, - "inode_gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "inode_uid": { - "ignore_above": 1024, - "type": "keyword" - }, - "invalid_context": { - "ignore_above": 1024, - "type": "keyword" - }, - "ioctlcmd": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "ignore_above": 1024, - "type": "keyword" - }, - "ipid": { - "ignore_above": 1024, - "type": "keyword" - }, - "ipx-net": { - "ignore_above": 1024, - "type": "keyword" - }, - "items": { - "ignore_above": 1024, - "type": "keyword" - }, - "iuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "ksize": { - "ignore_above": 1024, - "type": "keyword" - }, - "laddr": { - "ignore_above": 1024, - "type": "keyword" - }, - "len": { - "ignore_above": 1024, - "type": "keyword" - }, - "list": { - "ignore_above": 1024, - "type": "keyword" - }, - "lport": { - "ignore_above": 1024, - "type": "keyword" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "macproto": { - "ignore_above": 1024, - "type": "keyword" - }, - "maj": { - "ignore_above": 1024, - "type": "keyword" - }, - "major": { - "ignore_above": 1024, - "type": "keyword" - }, - "minor": { - "ignore_above": 1024, - "type": "keyword" - }, - "model": { - "ignore_above": 1024, - "type": "keyword" - }, - "msg": { - "ignore_above": 1024, - "type": "keyword" - }, - "nargs": { - "ignore_above": 1024, - "type": "keyword" - }, - "net": { - "ignore_above": 1024, - "type": "keyword" - }, - "new": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-chardev": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-disk": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-enabled": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-fs": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-level": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-log_passwd": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-mem": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-net": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-range": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-rng": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-role": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-seuser": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-vcpu": { - "ignore_above": 1024, - "type": "keyword" - }, - "new_gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "new_lock": { - "ignore_above": 1024, - "type": "keyword" - }, - "new_pe": { - "ignore_above": 1024, - "type": "keyword" - }, - "new_pi": { - "ignore_above": 1024, - "type": "keyword" - }, - "new_pp": { - "ignore_above": 1024, - "type": "keyword" - }, - "nlnk-fam": { - "ignore_above": 1024, - "type": "keyword" - }, - "nlnk-grp": { - "ignore_above": 1024, - "type": "keyword" - }, - "nlnk-pid": { - "ignore_above": 1024, - "type": "keyword" - }, - "oauid": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_uid": { - "ignore_above": 1024, - "type": "keyword" - }, - "ocomm": { - "ignore_above": 1024, - "type": "keyword" - }, - "oflag": { - "ignore_above": 1024, - "type": "keyword" - }, - "old": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-auid": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-chardev": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-disk": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-enabled": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-fs": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-level": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-log_passwd": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-mem": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-net": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-range": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-rng": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-role": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-ses": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-seuser": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-vcpu": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_enforcing": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_lock": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_pe": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_pi": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_pp": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_prom": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_val": { - "ignore_above": 1024, - "type": "keyword" - }, - "op": { - "ignore_above": 1024, - "type": "keyword" - }, - "opid": { - "ignore_above": 1024, - "type": "keyword" - }, - "oses": { - "ignore_above": 1024, - "type": "keyword" - }, - "outif": { - "ignore_above": 1024, - "type": "keyword" - }, - "parent": { - "ignore_above": 1024, - "type": "keyword" - }, - "per": { - "ignore_above": 1024, - "type": "keyword" - }, - "perm": { - "ignore_above": 1024, - "type": "keyword" - }, - "perm_mask": { - "ignore_above": 1024, - "type": "keyword" - }, - "permissive": { - "ignore_above": 1024, - "type": "keyword" - }, - "pfs": { - "ignore_above": 1024, - "type": "keyword" - }, - "printer": { - "ignore_above": 1024, - "type": "keyword" - }, - "prom": { - "ignore_above": 1024, - "type": "keyword" - }, - "proto": { - "ignore_above": 1024, - "type": "keyword" - }, - "qbytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "range": { - "ignore_above": 1024, - "type": "keyword" - }, - "reason": { - "ignore_above": 1024, - "type": "keyword" - }, - "removed": { - "ignore_above": 1024, - "type": "keyword" - }, - "res": { - "ignore_above": 1024, - "type": "keyword" - }, - "resrc": { - "ignore_above": 1024, - "type": "keyword" - }, - "rport": { - "ignore_above": 1024, - "type": "keyword" - }, - "sauid": { - "ignore_above": 1024, - "type": "keyword" - }, - "scontext": { - "ignore_above": 1024, - "type": "keyword" - }, - "selected-context": { - "ignore_above": 1024, - "type": "keyword" - }, - "seperm": { - "ignore_above": 1024, - "type": "keyword" - }, - "seperms": { - "ignore_above": 1024, - "type": "keyword" - }, - "seqno": { - "ignore_above": 1024, - "type": "keyword" - }, - "seresult": { - "ignore_above": 1024, - "type": "keyword" - }, - "ses": { - "ignore_above": 1024, - "type": "keyword" - }, - "seuser": { - "ignore_above": 1024, - "type": "keyword" - }, - "sig": { - "ignore_above": 1024, - "type": "keyword" - }, - "sigev_signo": { - "ignore_above": 1024, - "type": "keyword" - }, - "smac": { - "ignore_above": 1024, - "type": "keyword" - }, - "socket": { - "properties": { - "addr": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "ignore_above": 1024, - "type": "keyword" - }, - "saddr": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "spid": { - "ignore_above": 1024, - "type": "keyword" - }, - "sport": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "subj": { - "ignore_above": 1024, - "type": "keyword" - }, - "success": { - "ignore_above": 1024, - "type": "keyword" - }, - "syscall": { - "ignore_above": 1024, - "type": "keyword" - }, - "table": { - "ignore_above": 1024, - "type": "keyword" - }, - "tclass": { - "ignore_above": 1024, - "type": "keyword" - }, - "tcontext": { - "ignore_above": 1024, - "type": "keyword" - }, - "terminal": { - "ignore_above": 1024, - "type": "keyword" - }, - "tty": { - "ignore_above": 1024, - "type": "keyword" - }, - "unit": { - "ignore_above": 1024, - "type": "keyword" - }, - "uri": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "val": { - "ignore_above": 1024, - "type": "keyword" - }, - "ver": { - "ignore_above": 1024, - "type": "keyword" - }, - "virt": { - "ignore_above": 1024, - "type": "keyword" - }, - "vm": { - "ignore_above": 1024, - "type": "keyword" - }, - "vm-ctx": { - "ignore_above": 1024, - "type": "keyword" - }, - "vm-pid": { - "ignore_above": 1024, - "type": "keyword" - }, - "watch": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "message_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "paths": { - "properties": { - "dev": { - "ignore_above": 1024, - "type": "keyword" - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "item": { - "ignore_above": 1024, - "type": "keyword" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "nametype": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_level": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_role": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_user": { - "ignore_above": 1024, - "type": "keyword" - }, - "objtype": { - "ignore_above": 1024, - "type": "keyword" - }, - "ogid": { - "ignore_above": 1024, - "type": "keyword" - }, - "ouid": { - "ignore_above": 1024, - "type": "keyword" - }, - "rdev": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "result": { - "ignore_above": 1024, - "type": "keyword" - }, - "sequence": { - "type": "long" - }, - "session": { - "ignore_above": 1024, - "type": "keyword" - }, - "summary": { - "properties": { - "actor": { - "properties": { - "primary": { - "ignore_above": 1024, - "type": "keyword" - }, - "secondary": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "how": { - "ignore_above": 1024, - "type": "keyword" - }, - "object": { - "properties": { - "primary": { - "ignore_above": 1024, - "type": "keyword" - }, - "secondary": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "client": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "availability_zone": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "instance": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "machine": { - "properties": { - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "project": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "region": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "container": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "tag": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "runtime": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "destination": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "data": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ttl": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "header_flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "op_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "question": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "subdomain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "docker": { - "properties": { - "container": { - "properties": { - "labels": { - "type": "object" - } - } - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "norms": false, - "type": "text" - }, - "stack_trace": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "event": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingested": { - "type": "date" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "outcome": { - "ignore_above": 1024, - "type": "keyword" - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "fields": { - "type": "object" - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "directory": { - "ignore_above": 1024, - "type": "keyword" - }, - "drive_letter": { - "ignore_above": 1, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mtime": { - "type": "date" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "fields": { - "raw": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "owner": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "selinux": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "role": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "setgid": { - "type": "boolean" - }, - "setuid": { - "type": "boolean" - }, - "size": { - "type": "long" - }, - "target_path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geoip": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "properties": { - "blake2b_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "blake2b_384": { - "ignore_above": 1024, - "type": "keyword" - }, - "blake2b_512": { - "ignore_above": 1024, - "type": "keyword" - }, - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha384": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_384": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_512": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512_224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "xxh64": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "containerized": { - "type": "boolean" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "build": { - "ignore_above": 1024, - "type": "keyword" - }, - "codename": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "http": { - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "referrer": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "jolokia": { - "properties": { - "agent": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "secured": { - "type": "boolean" - }, - "server": { - "properties": { - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kubernetes": { - "properties": { - "annotations": { - "properties": { - "*": { - "type": "object" - } - } - }, - "container": { - "properties": { - "image": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "deployment": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "properties": { - "*": { - "type": "object" - } - } - }, - "namespace": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pod": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "replicaset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "statefulset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "labels": { - "type": "object" - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "network": { - "properties": { - "application": { - "ignore_above": 1024, - "type": "keyword" - }, - "bytes": { - "type": "long" - }, - "community_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "direction": { - "ignore_above": 1024, - "type": "keyword" - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "packets": { - "type": "long" - }, - "protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "observer": { - "properties": { - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "organization": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "package": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "build_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "checksum": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "install_scope": { - "ignore_above": 1024, - "type": "keyword" - }, - "installed": { - "type": "date" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "process": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "blake2b_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "blake2b_384": { - "ignore_above": 1024, - "type": "keyword" - }, - "blake2b_512": { - "ignore_above": 1024, - "type": "keyword" - }, - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha384": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_384": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_512": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512_224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "xxh64": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "parent": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "strings": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hive": { - "ignore_above": 1024, - "type": "keyword" - }, - "key": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "related": { - "properties": { - "ip": { - "type": "ip" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "ruleset": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "server": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "service": { - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "socket": { - "properties": { - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "system": { - "properties": { - "audit": { - "properties": { - "host": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "boottime": { - "type": "date" - }, - "containerized": { - "type": "boolean" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "codename": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timezone": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "offset": { - "properties": { - "sec": { - "type": "long" - } - } - } - } - }, - "uptime": { - "type": "long" - } - } - }, - "package": { - "properties": { - "arch": { - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "installtime": { - "type": "date" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "release": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "summary": { - "ignore_above": 1024, - "type": "keyword" - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user": { - "properties": { - "dir": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "type": "object" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "password": { - "properties": { - "last_changed": { - "type": "date" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "shell": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - }, - "user_information": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "tags": { - "ignore_above": 1024, - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "ignore_above": 1024, - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "timeseries": { - "properties": { - "instance": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tls": { - "properties": { - "cipher": { - "ignore_above": 1024, - "type": "keyword" - }, - "client": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - }, - "supported_ciphers": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "curve": { - "ignore_above": 1024, - "type": "keyword" - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3s": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_protocol": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tracing": { - "properties": { - "trace": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "transaction": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "url": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "fragment": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "password": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "query": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "scheme": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "username": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user": { - "properties": { - "audit": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "effective": { - "properties": { - "group": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "filesystem": { - "properties": { - "group": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "name_map": { - "type": "object" - }, - "saved": { - "properties": { - "group": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "selinux": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "role": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "terminal": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user_agent": { - "properties": { - "device": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vulnerability": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "classification": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "enumeration": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "report_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "scanner": { - "properties": { - "vendor": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "severity": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "settings": { - "index": { - "lifecycle": { - "name": "auditbeat", - "rollover_alias": "auditbeat-7.6.0" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "number_of_replicas": "1", - "number_of_shards": "1", - "query": { - "default_field": [ - "message", - "tags", - "agent.ephemeral_id", - "agent.id", - "agent.name", - "agent.type", - "agent.version", - "as.organization.name", - "client.address", - "client.as.organization.name", - "client.domain", - "client.geo.city_name", - "client.geo.continent_name", - "client.geo.country_iso_code", - "client.geo.country_name", - "client.geo.name", - "client.geo.region_iso_code", - "client.geo.region_name", - "client.mac", - "client.registered_domain", - "client.top_level_domain", - "client.user.domain", - "client.user.email", - "client.user.full_name", - "client.user.group.domain", - "client.user.group.id", - "client.user.group.name", - "client.user.hash", - "client.user.id", - "client.user.name", - "cloud.account.id", - "cloud.availability_zone", - "cloud.instance.id", - "cloud.instance.name", - "cloud.machine.type", - "cloud.provider", - "cloud.region", - "container.id", - "container.image.name", - "container.image.tag", - "container.name", - "container.runtime", - "destination.address", - "destination.as.organization.name", - "destination.domain", - "destination.geo.city_name", - "destination.geo.continent_name", - "destination.geo.country_iso_code", - "destination.geo.country_name", - "destination.geo.name", - "destination.geo.region_iso_code", - "destination.geo.region_name", - "destination.mac", - "destination.registered_domain", - "destination.top_level_domain", - "destination.user.domain", - "destination.user.email", - "destination.user.full_name", - "destination.user.group.domain", - "destination.user.group.id", - "destination.user.group.name", - "destination.user.hash", - "destination.user.id", - "destination.user.name", - "dns.answers.class", - "dns.answers.data", - "dns.answers.name", - "dns.answers.type", - "dns.header_flags", - "dns.id", - "dns.op_code", - "dns.question.class", - "dns.question.name", - "dns.question.registered_domain", - "dns.question.subdomain", - "dns.question.top_level_domain", - "dns.question.type", - "dns.response_code", - "dns.type", - "ecs.version", - "error.code", - "error.id", - "error.message", - "error.stack_trace", - "error.type", - "event.action", - "event.category", - "event.code", - "event.dataset", - "event.hash", - "event.id", - "event.kind", - "event.module", - "event.original", - "event.outcome", - "event.provider", - "event.timezone", - "event.type", - "file.device", - "file.directory", - "file.extension", - "file.gid", - "file.group", - "file.hash.md5", - "file.hash.sha1", - "file.hash.sha256", - "file.hash.sha512", - "file.inode", - "file.mode", - "file.name", - "file.owner", - "file.path", - "file.target_path", - "file.type", - "file.uid", - "geo.city_name", - "geo.continent_name", - "geo.country_iso_code", - "geo.country_name", - "geo.name", - "geo.region_iso_code", - "geo.region_name", - "group.domain", - "group.id", - "group.name", - "hash.md5", - "hash.sha1", - "hash.sha256", - "hash.sha512", - "host.architecture", - "host.geo.city_name", - "host.geo.continent_name", - "host.geo.country_iso_code", - "host.geo.country_name", - "host.geo.name", - "host.geo.region_iso_code", - "host.geo.region_name", - "host.hostname", - "host.id", - "host.mac", - "host.name", - "host.os.family", - "host.os.full", - "host.os.kernel", - "host.os.name", - "host.os.platform", - "host.os.version", - "host.type", - "host.user.domain", - "host.user.email", - "host.user.full_name", - "host.user.group.domain", - "host.user.group.id", - "host.user.group.name", - "host.user.hash", - "host.user.id", - "host.user.name", - "http.request.body.content", - "http.request.method", - "http.request.referrer", - "http.response.body.content", - "http.version", - "log.level", - "log.logger", - "log.origin.file.name", - "log.origin.function", - "log.original", - "log.syslog.facility.name", - "log.syslog.severity.name", - "network.application", - "network.community_id", - "network.direction", - "network.iana_number", - "network.name", - "network.protocol", - "network.transport", - "network.type", - "observer.geo.city_name", - "observer.geo.continent_name", - "observer.geo.country_iso_code", - "observer.geo.country_name", - "observer.geo.name", - "observer.geo.region_iso_code", - "observer.geo.region_name", - "observer.hostname", - "observer.mac", - "observer.name", - "observer.os.family", - "observer.os.full", - "observer.os.kernel", - "observer.os.name", - "observer.os.platform", - "observer.os.version", - "observer.product", - "observer.serial_number", - "observer.type", - "observer.vendor", - "observer.version", - "organization.id", - "organization.name", - "os.family", - "os.full", - "os.kernel", - "os.name", - "os.platform", - "os.version", - "package.architecture", - "package.checksum", - "package.description", - "package.install_scope", - "package.license", - "package.name", - "package.path", - "package.version", - "process.args", - "text", - "process.executable", - "process.hash.md5", - "process.hash.sha1", - "process.hash.sha256", - "process.hash.sha512", - "process.name", - "text", - "text", - "text", - "text", - "text", - "process.thread.name", - "process.title", - "process.working_directory", - "server.address", - "server.as.organization.name", - "server.domain", - "server.geo.city_name", - "server.geo.continent_name", - "server.geo.country_iso_code", - "server.geo.country_name", - "server.geo.name", - "server.geo.region_iso_code", - "server.geo.region_name", - "server.mac", - "server.registered_domain", - "server.top_level_domain", - "server.user.domain", - "server.user.email", - "server.user.full_name", - "server.user.group.domain", - "server.user.group.id", - "server.user.group.name", - "server.user.hash", - "server.user.id", - "server.user.name", - "service.ephemeral_id", - "service.id", - "service.name", - "service.node.name", - "service.state", - "service.type", - "service.version", - "source.address", - "source.as.organization.name", - "source.domain", - "source.geo.city_name", - "source.geo.continent_name", - "source.geo.country_iso_code", - "source.geo.country_name", - "source.geo.name", - "source.geo.region_iso_code", - "source.geo.region_name", - "source.mac", - "source.registered_domain", - "source.top_level_domain", - "source.user.domain", - "source.user.email", - "source.user.full_name", - "source.user.group.domain", - "source.user.group.id", - "source.user.group.name", - "source.user.hash", - "source.user.id", - "source.user.name", - "threat.framework", - "threat.tactic.id", - "threat.tactic.name", - "threat.tactic.reference", - "threat.technique.id", - "threat.technique.name", - "threat.technique.reference", - "tracing.trace.id", - "tracing.transaction.id", - "url.domain", - "url.extension", - "url.fragment", - "url.full", - "url.original", - "url.password", - "url.path", - "url.query", - "url.registered_domain", - "url.scheme", - "url.top_level_domain", - "url.username", - "user.domain", - "user.email", - "user.full_name", - "user.group.domain", - "user.group.id", - "user.group.name", - "user.hash", - "user.id", - "user.name", - "user_agent.device.name", - "user_agent.name", - "text", - "user_agent.original", - "user_agent.os.family", - "user_agent.os.full", - "user_agent.os.kernel", - "user_agent.os.name", - "user_agent.os.platform", - "user_agent.os.version", - "user_agent.version", - "text", - "agent.hostname", - "timeseries.instance", - "cloud.project.id", - "cloud.image.id", - "host.os.build", - "host.os.codename", - "kubernetes.pod.name", - "kubernetes.pod.uid", - "kubernetes.namespace", - "kubernetes.node.name", - "kubernetes.replicaset.name", - "kubernetes.deployment.name", - "kubernetes.statefulset.name", - "kubernetes.container.name", - "kubernetes.container.image", - "jolokia.agent.version", - "jolokia.agent.id", - "jolokia.server.product", - "jolokia.server.version", - "jolokia.server.vendor", - "jolokia.url", - "raw", - "file.origin", - "file.selinux.user", - "file.selinux.role", - "file.selinux.domain", - "file.selinux.level", - "user.audit.id", - "user.audit.name", - "user.effective.id", - "user.effective.name", - "user.effective.group.id", - "user.effective.group.name", - "user.filesystem.id", - "user.filesystem.name", - "user.filesystem.group.id", - "user.filesystem.group.name", - "user.saved.id", - "user.saved.name", - "user.saved.group.id", - "user.saved.group.name", - "user.selinux.user", - "user.selinux.role", - "user.selinux.domain", - "user.selinux.level", - "user.selinux.category", - "source.path", - "destination.path", - "auditd.message_type", - "auditd.session", - "auditd.result", - "auditd.summary.actor.primary", - "auditd.summary.actor.secondary", - "auditd.summary.object.type", - "auditd.summary.object.primary", - "auditd.summary.object.secondary", - "auditd.summary.how", - "auditd.paths.inode", - "auditd.paths.dev", - "auditd.paths.obj_user", - "auditd.paths.obj_role", - "auditd.paths.obj_domain", - "auditd.paths.obj_level", - "auditd.paths.objtype", - "auditd.paths.ouid", - "auditd.paths.rdev", - "auditd.paths.nametype", - "auditd.paths.ogid", - "auditd.paths.item", - "auditd.paths.mode", - "auditd.paths.name", - "auditd.data.action", - "auditd.data.minor", - "auditd.data.acct", - "auditd.data.addr", - "auditd.data.cipher", - "auditd.data.id", - "auditd.data.entries", - "auditd.data.kind", - "auditd.data.ksize", - "auditd.data.spid", - "auditd.data.arch", - "auditd.data.argc", - "auditd.data.major", - "auditd.data.unit", - "auditd.data.table", - "auditd.data.terminal", - "auditd.data.grantors", - "auditd.data.direction", - "auditd.data.op", - "auditd.data.tty", - "auditd.data.syscall", - "auditd.data.data", - "auditd.data.family", - "auditd.data.mac", - "auditd.data.pfs", - "auditd.data.items", - "auditd.data.a0", - "auditd.data.a1", - "auditd.data.a2", - "auditd.data.a3", - "auditd.data.hostname", - "auditd.data.lport", - "auditd.data.rport", - "auditd.data.exit", - "auditd.data.fp", - "auditd.data.laddr", - "auditd.data.sport", - "auditd.data.capability", - "auditd.data.nargs", - "auditd.data.new-enabled", - "auditd.data.audit_backlog_limit", - "auditd.data.dir", - "auditd.data.cap_pe", - "auditd.data.model", - "auditd.data.new_pp", - "auditd.data.old-enabled", - "auditd.data.oauid", - "auditd.data.old", - "auditd.data.banners", - "auditd.data.feature", - "auditd.data.vm-ctx", - "auditd.data.opid", - "auditd.data.seperms", - "auditd.data.seresult", - "auditd.data.new-rng", - "auditd.data.old-net", - "auditd.data.sigev_signo", - "auditd.data.ino", - "auditd.data.old_enforcing", - "auditd.data.old-vcpu", - "auditd.data.range", - "auditd.data.res", - "auditd.data.added", - "auditd.data.fam", - "auditd.data.nlnk-pid", - "auditd.data.subj", - "auditd.data.a[0-3]", - "auditd.data.cgroup", - "auditd.data.kernel", - "auditd.data.ocomm", - "auditd.data.new-net", - "auditd.data.permissive", - "auditd.data.class", - "auditd.data.compat", - "auditd.data.fi", - "auditd.data.changed", - "auditd.data.msg", - "auditd.data.dport", - "auditd.data.new-seuser", - "auditd.data.invalid_context", - "auditd.data.dmac", - "auditd.data.ipx-net", - "auditd.data.iuid", - "auditd.data.macproto", - "auditd.data.obj", - "auditd.data.ipid", - "auditd.data.new-fs", - "auditd.data.vm-pid", - "auditd.data.cap_pi", - "auditd.data.old-auid", - "auditd.data.oses", - "auditd.data.fd", - "auditd.data.igid", - "auditd.data.new-disk", - "auditd.data.parent", - "auditd.data.len", - "auditd.data.oflag", - "auditd.data.uuid", - "auditd.data.code", - "auditd.data.nlnk-grp", - "auditd.data.cap_fp", - "auditd.data.new-mem", - "auditd.data.seperm", - "auditd.data.enforcing", - "auditd.data.new-chardev", - "auditd.data.old-rng", - "auditd.data.outif", - "auditd.data.cmd", - "auditd.data.hook", - "auditd.data.new-level", - "auditd.data.sauid", - "auditd.data.sig", - "auditd.data.audit_backlog_wait_time", - "auditd.data.printer", - "auditd.data.old-mem", - "auditd.data.perm", - "auditd.data.old_pi", - "auditd.data.state", - "auditd.data.format", - "auditd.data.new_gid", - "auditd.data.tcontext", - "auditd.data.maj", - "auditd.data.watch", - "auditd.data.device", - "auditd.data.grp", - "auditd.data.bool", - "auditd.data.icmp_type", - "auditd.data.new_lock", - "auditd.data.old_prom", - "auditd.data.acl", - "auditd.data.ip", - "auditd.data.new_pi", - "auditd.data.default-context", - "auditd.data.inode_gid", - "auditd.data.new-log_passwd", - "auditd.data.new_pe", - "auditd.data.selected-context", - "auditd.data.cap_fver", - "auditd.data.file", - "auditd.data.net", - "auditd.data.virt", - "auditd.data.cap_pp", - "auditd.data.old-range", - "auditd.data.resrc", - "auditd.data.new-range", - "auditd.data.obj_gid", - "auditd.data.proto", - "auditd.data.old-disk", - "auditd.data.audit_failure", - "auditd.data.inif", - "auditd.data.vm", - "auditd.data.flags", - "auditd.data.nlnk-fam", - "auditd.data.old-fs", - "auditd.data.old-ses", - "auditd.data.seqno", - "auditd.data.fver", - "auditd.data.qbytes", - "auditd.data.seuser", - "auditd.data.cap_fe", - "auditd.data.new-vcpu", - "auditd.data.old-level", - "auditd.data.old_pp", - "auditd.data.daddr", - "auditd.data.old-role", - "auditd.data.ioctlcmd", - "auditd.data.smac", - "auditd.data.apparmor", - "auditd.data.fe", - "auditd.data.perm_mask", - "auditd.data.ses", - "auditd.data.cap_fi", - "auditd.data.obj_uid", - "auditd.data.reason", - "auditd.data.list", - "auditd.data.old_lock", - "auditd.data.bus", - "auditd.data.old_pe", - "auditd.data.new-role", - "auditd.data.prom", - "auditd.data.uri", - "auditd.data.audit_enabled", - "auditd.data.old-log_passwd", - "auditd.data.old-seuser", - "auditd.data.per", - "auditd.data.scontext", - "auditd.data.tclass", - "auditd.data.ver", - "auditd.data.new", - "auditd.data.val", - "auditd.data.img-ctx", - "auditd.data.old-chardev", - "auditd.data.old_val", - "auditd.data.success", - "auditd.data.inode_uid", - "auditd.data.removed", - "auditd.data.socket.port", - "auditd.data.socket.saddr", - "auditd.data.socket.addr", - "auditd.data.socket.family", - "auditd.data.socket.path", - "geoip.continent_name", - "geoip.city_name", - "geoip.region_name", - "geoip.country_iso_code", - "hash.blake2b_256", - "hash.blake2b_384", - "hash.blake2b_512", - "hash.md5", - "hash.sha1", - "hash.sha224", - "hash.sha256", - "hash.sha384", - "hash.sha3_224", - "hash.sha3_256", - "hash.sha3_384", - "hash.sha3_512", - "hash.sha512", - "hash.sha512_224", - "hash.sha512_256", - "hash.xxh64", - "event.origin", - "user.entity_id", - "user.terminal", - "process.entity_id", - "process.hash.blake2b_256", - "process.hash.blake2b_384", - "process.hash.blake2b_512", - "process.hash.sha224", - "process.hash.sha384", - "process.hash.sha3_224", - "process.hash.sha3_256", - "process.hash.sha3_384", - "process.hash.sha3_512", - "process.hash.sha512_224", - "process.hash.sha512_256", - "process.hash.xxh64", - "socket.entity_id", - "system.audit.host.timezone.name", - "system.audit.host.hostname", - "system.audit.host.id", - "system.audit.host.architecture", - "system.audit.host.mac", - "system.audit.host.os.codename", - "system.audit.host.os.platform", - "system.audit.host.os.name", - "system.audit.host.os.family", - "system.audit.host.os.version", - "system.audit.host.os.kernel", - "system.audit.package.entity_id", - "system.audit.package.name", - "system.audit.package.version", - "system.audit.package.release", - "system.audit.package.arch", - "system.audit.package.license", - "system.audit.package.summary", - "system.audit.package.url", - "system.audit.user.name", - "system.audit.user.uid", - "system.audit.user.gid", - "system.audit.user.dir", - "system.audit.user.shell", - "system.audit.user.user_information", - "system.audit.user.password.type", - "fields.*" - ] - }, - "refresh_interval": "5s" - } - } - } -} diff --git a/x-pack/test/security_solution_cypress/es_archives/custom_rules/data.json.gz b/x-pack/test/security_solution_cypress/es_archives/custom_rules/data.json.gz deleted file mode 100644 index fb262155ea03a..0000000000000 Binary files a/x-pack/test/security_solution_cypress/es_archives/custom_rules/data.json.gz and /dev/null differ diff --git a/x-pack/test/security_solution_cypress/es_archives/custom_rules/mappings.json b/x-pack/test/security_solution_cypress/es_archives/custom_rules/mappings.json deleted file mode 100644 index a6b171cdfd7d9..0000000000000 --- a/x-pack/test/security_solution_cypress/es_archives/custom_rules/mappings.json +++ /dev/null @@ -1,6243 +0,0 @@ -{ - "type": "index", - "value": { - "aliases": { - ".kibana": { - } - }, - "index": ".kibana_1", - "mappings": { - "_meta": { - "migrationMappingPropertyHashes": { - "action": "6e96ac5e648f57523879661ea72525b7", - "action_task_params": "a9d49f184ee89641044be0ca2950fa3a", - "agent_configs": "38abaf89513877745c359e7700c0c66a", - "agent_events": "3231653fafe4ef3196fe3b32ab774bf2", - "agents": "75c0f4a11560dbc38b65e5e1d98fc9da", - "alert": "7b44fba6773e37c806ce290ea9b7024e", - "apm-indices": "9bb9b2bf1fa636ed8619cbab5ce6a1dd", - "apm-telemetry": "e8619030e08b671291af04c4603b4944", - "application_usage_totals": "c897e4310c5f24b07caaff3db53ae2c1", - "application_usage_transactional": "965839e75f809fefe04f92dc4d99722a", - "canvas-element": "7390014e1091044523666d97247392fc", - "canvas-workpad": "b0a1706d356228dbdcb4a17e6b9eb231", - "cases": "08b8b110dbca273d37e8aef131ecab61", - "cases-comments": "df3c1aa1b3dd5737c94d9e430b13c48a", - "cases-configure": "42711cbb311976c0687853f4c1354572", - "cases-user-actions": "32277330ec6b721abe3b846cfd939a71", - "config": "ae24d22d5986d04124cc6568f771066f", - "dashboard": "d00f614b29a80360e1190193fd333bab", - "datasources": "d4bc0c252b2b5683ff21ea32d00acffc", - "enrollment_api_keys": "28b91e20b105b6f928e2012600085d8f", - "epm-package": "75d12cd13c867fd713d7dfb27366bc20", - "file-upload-telemetry": "0ed4d3e1983d1217a30982630897092e", - "graph-workspace": "cd7ba1330e6682e9cc00b78850874be1", - "index-pattern": "66eccb05066c5a89924f48a9e9736499", - "infrastructure-ui-source": "ddc0ecb18383f6b26101a2fadb2dab0c", - "inventory-view": "9ecce5b58867403613d82fe496470b34", - "kql-telemetry": "d12a98a6f19a2d273696597547e064ee", - "lens": "21c3ea0763beb1ecb0162529706b88c5", - "lens-ui-telemetry": "509bfa5978586998e05f9e303c07a327", - "map": "23d7aa4a720d4938ccde3983f87bd58d", - "maps-telemetry": "268da3a48066123fc5baf35abaa55014", - "metrics-explorer-view": "53c5365793677328df0ccb6138bf3cdd", - "migrationVersion": "4a1746014a75ade3a714e1db5763276f", - "ml-telemetry": "257fd1d4b4fdbb9cb4b8a3b27da201e9", - "namespace": "2f4316de49999235636386fe51dc06c1", - "outputs": "aee9782e0d500b867859650a36280165", - "query": "11aaeb7f5f7fa5bb43f25e18ce26e7d9", - "references": "7997cf5a56cc02bdc9c93361bde732b0", - "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", - "search": "181661168bbadd1eff5902361e2a0d5c", - "server": "ec97f1c5da1a19609a60874e5af1100c", - "siem-detection-engine-rule-status": "ae783f41c6937db6b7a2ef5c93a9e9b0", - "siem-ui-timeline": "ac8020190f5950dd3250b6499144e7fb", - "siem-ui-timeline-note": "8874706eedc49059d4cf0f5094559084", - "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", - "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", - "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", - "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", - "type": "2f4316de49999235636386fe51dc06c1", - "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", - "updated_at": "00da57df13e94e9d98437d13ace4bfe0", - "upgrade-assistant-reindex-operation": "a53a20fe086b72c9a86da3cc12dad8a6", - "upgrade-assistant-telemetry": "56702cec857e0a9dacfb696655b4ff7b", - "uptime-dynamic-settings": "b6289473c8985c79b6c47eebc19a0ca5", - "url": "c7f66a0df8b1b52f17c28c4adb111105", - "visualization": "52d7a13ad68a150c4525b292d23e12cc" - } - }, - "dynamic": "strict", - "properties": { - "action": { - "properties": { - "actionTypeId": { - "type": "keyword" - }, - "config": { - "enabled": false, - "type": "object" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - }, - "secrets": { - "type": "binary" - } - } - }, - "action_task_params": { - "properties": { - "actionId": { - "type": "keyword" - }, - "apiKey": { - "type": "binary" - }, - "params": { - "enabled": false, - "type": "object" - } - } - }, - "agent_configs": { - "properties": { - "datasources": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "id": { - "type": "keyword" - }, - "is_default": { - "type": "boolean" - }, - "name": { - "type": "text" - }, - "namespace": { - "type": "keyword" - }, - "revision": { - "type": "integer" - }, - "status": { - "type": "keyword" - }, - "updated_by": { - "type": "keyword" - }, - "updated_on": { - "type": "keyword" - } - } - }, - "agent_events": { - "properties": { - "action_id": { - "type": "keyword" - }, - "agent_id": { - "type": "keyword" - }, - "config_id": { - "type": "keyword" - }, - "data": { - "type": "text" - }, - "message": { - "type": "text" - }, - "payload": { - "type": "text" - }, - "stream_id": { - "type": "keyword" - }, - "subtype": { - "type": "keyword" - }, - "timestamp": { - "type": "date" - }, - "type": { - "type": "keyword" - } - } - }, - "agents": { - "properties": { - "access_api_key_id": { - "type": "keyword" - }, - "actions": { - "properties": { - "created_at": { - "type": "date" - }, - "data": { - "type": "text" - }, - "id": { - "type": "keyword" - }, - "sent_at": { - "type": "date" - }, - "type": { - "type": "keyword" - } - }, - "type": "nested" - }, - "active": { - "type": "boolean" - }, - "config_id": { - "type": "keyword" - }, - "config_newest_revision": { - "type": "integer" - }, - "config_revision": { - "type": "integer" - }, - "current_error_events": { - "type": "text" - }, - "default_api_key": { - "type": "keyword" - }, - "enrolled_at": { - "type": "date" - }, - "last_checkin": { - "type": "date" - }, - "last_updated": { - "type": "date" - }, - "local_metadata": { - "type": "text" - }, - "shared_id": { - "type": "keyword" - }, - "type": { - "type": "keyword" - }, - "updated_at": { - "type": "date" - }, - "user_provided_metadata": { - "type": "text" - }, - "version": { - "type": "keyword" - } - } - }, - "alert": { - "properties": { - "actions": { - "properties": { - "actionRef": { - "type": "keyword" - }, - "actionTypeId": { - "type": "keyword" - }, - "group": { - "type": "keyword" - }, - "params": { - "enabled": false, - "type": "object" - } - }, - "type": "nested" - }, - "alertTypeId": { - "type": "keyword" - }, - "apiKey": { - "type": "binary" - }, - "apiKeyOwner": { - "type": "keyword" - }, - "consumer": { - "type": "keyword" - }, - "createdAt": { - "type": "date" - }, - "createdBy": { - "type": "keyword" - }, - "enabled": { - "type": "boolean" - }, - "muteAll": { - "type": "boolean" - }, - "mutedInstanceIds": { - "type": "keyword" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - }, - "params": { - "enabled": false, - "type": "object" - }, - "schedule": { - "properties": { - "interval": { - "type": "keyword" - } - } - }, - "scheduledTaskId": { - "type": "keyword" - }, - "tags": { - "type": "keyword" - }, - "throttle": { - "type": "keyword" - }, - "updatedAt": { - "type": "date" - }, - "updatedBy": { - "type": "keyword" - }, - "executionStatus": { - "properties": { - "status": { - "type": "keyword" - }, - "lastExecutionDate": { - "type": "date" - }, - "error": { - "properties": { - "reason": { - "type": "keyword" - }, - "message": { - "type": "keyword" - } - } - } - } - } - } - }, - "apm-indices": { - "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } - } - } - }, - "apm-telemetry": { - "properties": { - "agents": { - "properties": { - "dotnet": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "name": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - } - } - } - } - }, - "go": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - } - } - } - } - }, - "java": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - } - } - } - } - }, - "js-base": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - } - } - } - } - }, - "nodejs": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - } - } - } - } - }, - "python": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - } - } - } - } - }, - "ruby": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - } - } - } - } - }, - "rum-js": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - } - } - } - } - } - } - }, - "cardinality": { - "properties": { - "transaction": { - "properties": { - "name": { - "properties": { - "all_agents": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "rum": { - "properties": { - "1d": { - "type": "long" - } - } - } - } - } - } - }, - "user_agent": { - "properties": { - "original": { - "properties": { - "all_agents": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "rum": { - "properties": { - "1d": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "counts": { - "properties": { - "agent_configuration": { - "properties": { - "all": { - "type": "long" - } - } - }, - "error": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "max_error_groups_per_service": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "max_transaction_groups_per_service": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "metric": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "onboarding": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "services": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "sourcemap": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "span": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "traces": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "transaction": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - } - } - }, - "has_any_services": { - "type": "boolean" - }, - "indices": { - "properties": { - "all": { - "properties": { - "total": { - "properties": { - "docs": { - "properties": { - "count": { - "type": "long" - } - } - }, - "store": { - "properties": { - "size_in_bytes": { - "type": "long" - } - } - } - } - } - } - }, - "shards": { - "properties": { - "total": { - "type": "long" - } - } - } - } - }, - "integrations": { - "properties": { - "ml": { - "properties": { - "all_jobs_count": { - "type": "long" - } - } - } - } - }, - "retainment": { - "properties": { - "error": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "metric": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "onboarding": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "span": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "transaction": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "services_per_agent": { - "properties": { - "dotnet": { - "null_value": 0, - "type": "long" - }, - "go": { - "null_value": 0, - "type": "long" - }, - "java": { - "null_value": 0, - "type": "long" - }, - "js-base": { - "null_value": 0, - "type": "long" - }, - "nodejs": { - "null_value": 0, - "type": "long" - }, - "python": { - "null_value": 0, - "type": "long" - }, - "ruby": { - "null_value": 0, - "type": "long" - }, - "rum-js": { - "null_value": 0, - "type": "long" - } - } - }, - "tasks": { - "properties": { - "agent_configuration": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "agents": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "cardinality": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "groupings": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "indices_stats": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "integrations": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "processor_events": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "services": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "versions": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "version": { - "properties": { - "apm_server": { - "properties": { - "major": { - "type": "long" - }, - "minor": { - "type": "long" - }, - "patch": { - "type": "long" - } - } - } - } - } - } - }, - "application_usage_totals": { - "properties": { - "appId": { - "type": "keyword" - }, - "minutesOnScreen": { - "type": "float" - }, - "numberOfClicks": { - "type": "long" - } - } - }, - "application_usage_transactional": { - "properties": { - "appId": { - "type": "keyword" - }, - "minutesOnScreen": { - "type": "float" - }, - "numberOfClicks": { - "type": "long" - }, - "timestamp": { - "type": "date" - } - } - }, - "canvas-element": { - "dynamic": "false", - "properties": { - "@created": { - "type": "date" - }, - "@timestamp": { - "type": "date" - }, - "content": { - "type": "text" - }, - "help": { - "type": "text" - }, - "image": { - "type": "text" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "canvas-workpad": { - "dynamic": "false", - "properties": { - "@created": { - "type": "date" - }, - "@timestamp": { - "type": "date" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "cases": { - "properties": { - "closed_at": { - "type": "date" - }, - "closed_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "created_at": { - "type": "date" - }, - "created_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "description": { - "type": "text" - }, - "external_service": { - "properties": { - "connector_id": { - "type": "keyword" - }, - "connector_name": { - "type": "keyword" - }, - "external_id": { - "type": "keyword" - }, - "external_title": { - "type": "text" - }, - "external_url": { - "type": "text" - }, - "pushed_at": { - "type": "date" - }, - "pushed_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - } - } - }, - "status": { - "type": "keyword" - }, - "tags": { - "type": "keyword" - }, - "title": { - "type": "keyword" - }, - "updated_at": { - "type": "date" - }, - "updated_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - } - } - }, - "cases-comments": { - "properties": { - "comment": { - "type": "text" - }, - "created_at": { - "type": "date" - }, - "created_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "pushed_at": { - "type": "date" - }, - "pushed_by": { - "properties": { - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "updated_at": { - "type": "date" - }, - "updated_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - } - } - }, - "cases-configure": { - "properties": { - "closure_type": { - "type": "keyword" - }, - "connector_id": { - "type": "keyword" - }, - "connector_name": { - "type": "keyword" - }, - "created_at": { - "type": "date" - }, - "created_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "updated_at": { - "type": "date" - }, - "updated_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - } - } - }, - "cases-user-actions": { - "properties": { - "action": { - "type": "keyword" - }, - "action_at": { - "type": "date" - }, - "action_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "action_field": { - "type": "keyword" - }, - "new_value": { - "type": "text" - }, - "old_value": { - "type": "text" - } - } - }, - "config": { - "dynamic": "true", - "properties": { - "buildNum": { - "type": "keyword" - }, - "dateFormat:tz": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "dashboard": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "optionsJSON": { - "type": "text" - }, - "panelsJSON": { - "type": "text" - }, - "refreshInterval": { - "properties": { - "display": { - "type": "keyword" - }, - "pause": { - "type": "boolean" - }, - "section": { - "type": "integer" - }, - "value": { - "type": "integer" - } - } - }, - "timeFrom": { - "type": "keyword" - }, - "timeRestore": { - "type": "boolean" - }, - "timeTo": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "datasources": { - "properties": { - "config_id": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "enabled": { - "type": "boolean" - }, - "inputs": { - "properties": { - "config": { - "type": "flattened" - }, - "enabled": { - "type": "boolean" - }, - "processors": { - "type": "keyword" - }, - "streams": { - "properties": { - "config": { - "type": "flattened" - }, - "dataset": { - "type": "keyword" - }, - "enabled": { - "type": "boolean" - }, - "id": { - "type": "keyword" - }, - "processors": { - "type": "keyword" - } - }, - "type": "nested" - }, - "type": { - "type": "keyword" - } - }, - "type": "nested" - }, - "name": { - "type": "keyword" - }, - "namespace": { - "type": "keyword" - }, - "output_id": { - "type": "keyword" - }, - "package": { - "properties": { - "name": { - "type": "keyword" - }, - "title": { - "type": "keyword" - }, - "version": { - "type": "keyword" - } - } - }, - "revision": { - "type": "integer" - } - } - }, - "enrollment_api_keys": { - "properties": { - "active": { - "type": "boolean" - }, - "api_key": { - "type": "binary" - }, - "api_key_id": { - "type": "keyword" - }, - "config_id": { - "type": "keyword" - }, - "created_at": { - "type": "date" - }, - "expire_at": { - "type": "date" - }, - "name": { - "type": "keyword" - }, - "type": { - "type": "keyword" - }, - "updated_at": { - "type": "date" - } - } - }, - "epm-package": { - "properties": { - "installed": { - "properties": { - "id": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - }, - "type": "nested" - }, - "name": { - "type": "keyword" - }, - "version": { - "type": "keyword" - } - } - }, - "file-upload-telemetry": { - "properties": { - "filesUploadedTotalCount": { - "type": "long" - } - } - }, - "graph-workspace": { - "properties": { - "description": { - "type": "text" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "numLinks": { - "type": "integer" - }, - "numVertices": { - "type": "integer" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - }, - "wsState": { - "type": "text" - } - } - }, - "index-pattern": { - "properties": { - "fieldFormatMap": { - "type": "text" - }, - "fields": { - "type": "text" - }, - "intervalName": { - "type": "keyword" - }, - "notExpandable": { - "type": "boolean" - }, - "sourceFilters": { - "type": "text" - }, - "timeFieldName": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "type": { - "type": "keyword" - }, - "typeMeta": { - "type": "keyword" - } - } - }, - "infrastructure-ui-source": { - "properties": { - "description": { - "type": "text" - }, - "fields": { - "properties": { - "container": { - "type": "keyword" - }, - "host": { - "type": "keyword" - }, - "pod": { - "type": "keyword" - }, - "tiebreaker": { - "type": "keyword" - }, - "timestamp": { - "type": "keyword" - } - } - }, - "logAlias": { - "type": "keyword" - }, - "logColumns": { - "properties": { - "fieldColumn": { - "properties": { - "field": { - "type": "keyword" - }, - "id": { - "type": "keyword" - } - } - }, - "messageColumn": { - "properties": { - "id": { - "type": "keyword" - } - } - }, - "timestampColumn": { - "properties": { - "id": { - "type": "keyword" - } - } - } - }, - "type": "nested" - }, - "metricAlias": { - "type": "keyword" - }, - "name": { - "type": "text" - } - } - }, - "inventory-view": { - "properties": { - "autoBounds": { - "type": "boolean" - }, - "autoReload": { - "type": "boolean" - }, - "boundsOverride": { - "properties": { - "max": { - "type": "integer" - }, - "min": { - "type": "integer" - } - } - }, - "customMetrics": { - "properties": { - "aggregation": { - "type": "keyword" - }, - "field": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "label": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - }, - "type": "nested" - }, - "customOptions": { - "properties": { - "field": { - "type": "keyword" - }, - "text": { - "type": "keyword" - } - }, - "type": "nested" - }, - "filterQuery": { - "properties": { - "expression": { - "type": "keyword" - }, - "kind": { - "type": "keyword" - } - } - }, - "groupBy": { - "properties": { - "field": { - "type": "keyword" - }, - "label": { - "type": "keyword" - } - }, - "type": "nested" - }, - "metric": { - "properties": { - "aggregation": { - "type": "keyword" - }, - "field": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "label": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "name": { - "type": "keyword" - }, - "nodeType": { - "type": "keyword" - }, - "time": { - "type": "integer" - }, - "view": { - "type": "keyword" - } - } - }, - "kql-telemetry": { - "properties": { - "optInCount": { - "type": "long" - }, - "optOutCount": { - "type": "long" - } - } - }, - "lens": { - "properties": { - "expression": { - "index": false, - "type": "keyword" - }, - "state": { - "type": "flattened" - }, - "title": { - "type": "text" - }, - "visualizationType": { - "type": "keyword" - } - } - }, - "lens-ui-telemetry": { - "properties": { - "count": { - "type": "integer" - }, - "date": { - "type": "date" - }, - "name": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "map": { - "properties": { - "bounds": { - "type": "geo_shape" - }, - "description": { - "type": "text" - }, - "layerListJSON": { - "type": "text" - }, - "mapStateJSON": { - "type": "text" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "maps-telemetry": { - "properties": { - "attributesPerMap": { - "properties": { - "dataSourcesCount": { - "properties": { - "avg": { - "type": "long" - }, - "max": { - "type": "long" - }, - "min": { - "type": "long" - } - } - }, - "emsVectorLayersCount": { - "dynamic": "true", - "type": "object" - }, - "layerTypesCount": { - "dynamic": "true", - "type": "object" - }, - "layersCount": { - "properties": { - "avg": { - "type": "long" - }, - "max": { - "type": "long" - }, - "min": { - "type": "long" - } - } - } - } - }, - "indexPatternsWithGeoFieldCount": { - "type": "long" - }, - "mapsTotalCount": { - "type": "long" - }, - "settings": { - "properties": { - "showMapVisualizationTypes": { - "type": "boolean" - } - } - }, - "timeCaptured": { - "type": "date" - } - } - }, - "metrics-explorer-view": { - "properties": { - "chartOptions": { - "properties": { - "stack": { - "type": "boolean" - }, - "type": { - "type": "keyword" - }, - "yAxisMode": { - "type": "keyword" - } - } - }, - "currentTimerange": { - "properties": { - "from": { - "type": "keyword" - }, - "interval": { - "type": "keyword" - }, - "to": { - "type": "keyword" - } - } - }, - "name": { - "type": "keyword" - }, - "options": { - "properties": { - "aggregation": { - "type": "keyword" - }, - "filterQuery": { - "type": "keyword" - }, - "groupBy": { - "type": "keyword" - }, - "limit": { - "type": "integer" - }, - "metrics": { - "properties": { - "aggregation": { - "type": "keyword" - }, - "color": { - "type": "keyword" - }, - "field": { - "type": "keyword" - }, - "label": { - "type": "keyword" - } - }, - "type": "nested" - } - } - } - } - }, - "migrationVersion": { - "dynamic": "true", - "properties": { - "space": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "ml-telemetry": { - "properties": { - "file_data_visualizer": { - "properties": { - "index_creation_count": { - "type": "long" - } - } - } - } - }, - "namespace": { - "type": "keyword" - }, - "outputs": { - "properties": { - "api_key": { - "type": "keyword" - }, - "ca_sha256": { - "type": "keyword" - }, - "config": { - "type": "flattened" - }, - "fleet_enroll_password": { - "type": "binary" - }, - "fleet_enroll_username": { - "type": "binary" - }, - "hosts": { - "type": "keyword" - }, - "is_default": { - "type": "boolean" - }, - "name": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "query": { - "properties": { - "description": { - "type": "text" - }, - "filters": { - "enabled": false, - "type": "object" - }, - "query": { - "properties": { - "language": { - "type": "keyword" - }, - "query": { - "index": false, - "type": "keyword" - } - } - }, - "timefilter": { - "enabled": false, - "type": "object" - }, - "title": { - "type": "text" - } - } - }, - "references": { - "properties": { - "id": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - }, - "type": "nested" - }, - "sample-data-telemetry": { - "properties": { - "installCount": { - "type": "long" - }, - "unInstallCount": { - "type": "long" - } - } - }, - "search": { - "properties": { - "columns": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "sort": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "server": { - "properties": { - "uuid": { - "type": "keyword" - } - } - }, - "siem-detection-engine-rule-status": { - "properties": { - "alertId": { - "type": "keyword" - }, - "bulkCreateTimeDurations": { - "type": "float" - }, - "gap": { - "type": "text" - }, - "lastFailureAt": { - "type": "date" - }, - "lastFailureMessage": { - "type": "text" - }, - "lastLookBackDate": { - "type": "date" - }, - "lastSuccessAt": { - "type": "date" - }, - "lastSuccessMessage": { - "type": "text" - }, - "searchAfterTimeDurations": { - "type": "float" - }, - "status": { - "type": "keyword" - }, - "statusDate": { - "type": "date" - } - } - }, - "siem-ui-timeline": { - "properties": { - "columns": { - "properties": { - "aggregatable": { - "type": "boolean" - }, - "category": { - "type": "keyword" - }, - "columnHeaderType": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "example": { - "type": "text" - }, - "id": { - "type": "keyword" - }, - "indexes": { - "type": "keyword" - }, - "name": { - "type": "text" - }, - "placeholder": { - "type": "text" - }, - "searchable": { - "type": "boolean" - }, - "type": { - "type": "keyword" - } - } - }, - "created": { - "type": "date" - }, - "createdBy": { - "type": "text" - }, - "dataProviders": { - "properties": { - "and": { - "properties": { - "enabled": { - "type": "boolean" - }, - "excluded": { - "type": "boolean" - }, - "id": { - "type": "keyword" - }, - "kqlQuery": { - "type": "text" - }, - "name": { - "type": "text" - }, - "queryMatch": { - "properties": { - "displayField": { - "type": "text" - }, - "displayValue": { - "type": "text" - }, - "field": { - "type": "text" - }, - "operator": { - "type": "text" - }, - "value": { - "type": "text" - } - } - } - } - }, - "enabled": { - "type": "boolean" - }, - "excluded": { - "type": "boolean" - }, - "id": { - "type": "keyword" - }, - "kqlQuery": { - "type": "text" - }, - "name": { - "type": "text" - }, - "queryMatch": { - "properties": { - "displayField": { - "type": "text" - }, - "displayValue": { - "type": "text" - }, - "field": { - "type": "text" - }, - "operator": { - "type": "text" - }, - "value": { - "type": "text" - } - } - } - } - }, - "dateRange": { - "properties": { - "end": { - "type": "date" - }, - "start": { - "type": "date" - } - } - }, - "description": { - "type": "text" - }, - "eventType": { - "type": "keyword" - }, - "favorite": { - "properties": { - "favoriteDate": { - "type": "date" - }, - "fullName": { - "type": "text" - }, - "keySearch": { - "type": "text" - }, - "userName": { - "type": "text" - } - } - }, - "filters": { - "properties": { - "exists": { - "type": "text" - }, - "match_all": { - "type": "text" - }, - "meta": { - "properties": { - "alias": { - "type": "text" - }, - "controlledBy": { - "type": "text" - }, - "disabled": { - "type": "boolean" - }, - "field": { - "type": "text" - }, - "formattedValue": { - "type": "text" - }, - "index": { - "type": "keyword" - }, - "key": { - "type": "keyword" - }, - "negate": { - "type": "boolean" - }, - "params": { - "type": "text" - }, - "type": { - "type": "keyword" - }, - "value": { - "type": "text" - } - } - }, - "missing": { - "type": "text" - }, - "query": { - "type": "text" - }, - "range": { - "type": "text" - }, - "script": { - "type": "text" - } - } - }, - "kqlMode": { - "type": "keyword" - }, - "kqlQuery": { - "properties": { - "filterQuery": { - "properties": { - "kuery": { - "properties": { - "expression": { - "type": "text" - }, - "kind": { - "type": "keyword" - } - } - }, - "serializedQuery": { - "type": "text" - } - } - } - } - }, - "savedQueryId": { - "type": "keyword" - }, - "sort": { - "properties": { - "columnId": { - "type": "keyword" - }, - "sortDirection": { - "type": "keyword" - } - } - }, - "title": { - "type": "text" - }, - "updated": { - "type": "date" - }, - "updatedBy": { - "type": "text" - } - } - }, - "siem-ui-timeline-note": { - "properties": { - "created": { - "type": "date" - }, - "createdBy": { - "type": "text" - }, - "eventId": { - "type": "keyword" - }, - "note": { - "type": "text" - }, - "timelineId": { - "type": "keyword" - }, - "updated": { - "type": "date" - }, - "updatedBy": { - "type": "text" - } - } - }, - "siem-ui-timeline-pinned-event": { - "properties": { - "created": { - "type": "date" - }, - "createdBy": { - "type": "text" - }, - "eventId": { - "type": "keyword" - }, - "timelineId": { - "type": "keyword" - }, - "updated": { - "type": "date" - }, - "updatedBy": { - "type": "text" - } - } - }, - "space": { - "properties": { - "_reserved": { - "type": "boolean" - }, - "color": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "disabledFeatures": { - "type": "keyword" - }, - "imageUrl": { - "index": false, - "type": "text" - }, - "initials": { - "type": "keyword" - }, - "name": { - "fields": { - "keyword": { - "ignore_above": 2048, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "spaceId": { - "type": "keyword" - }, - "telemetry": { - "properties": { - "allowChangingOptInStatus": { - "type": "boolean" - }, - "enabled": { - "type": "boolean" - }, - "lastReported": { - "type": "date" - }, - "lastVersionChecked": { - "type": "keyword" - }, - "reportFailureCount": { - "type": "integer" - }, - "reportFailureVersion": { - "type": "keyword" - }, - "sendUsageFrom": { - "type": "keyword" - }, - "userHasSeenNotice": { - "type": "boolean" - } - } - }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "tsvb-validation-telemetry": { - "properties": { - "failedRequests": { - "type": "long" - } - } - }, - "type": { - "type": "keyword" - }, - "ui-metric": { - "properties": { - "count": { - "type": "integer" - } - } - }, - "updated_at": { - "type": "date" - }, - "upgrade-assistant-reindex-operation": { - "dynamic": "true", - "properties": { - "indexName": { - "type": "keyword" - }, - "status": { - "type": "integer" - } - } - }, - "upgrade-assistant-telemetry": { - "properties": { - "features": { - "properties": { - "deprecation_logging": { - "properties": { - "enabled": { - "null_value": true, - "type": "boolean" - } - } - } - } - }, - "ui_open": { - "properties": { - "cluster": { - "null_value": 0, - "type": "long" - }, - "indices": { - "null_value": 0, - "type": "long" - }, - "overview": { - "null_value": 0, - "type": "long" - } - } - }, - "ui_reindex": { - "properties": { - "close": { - "null_value": 0, - "type": "long" - }, - "open": { - "null_value": 0, - "type": "long" - }, - "start": { - "null_value": 0, - "type": "long" - }, - "stop": { - "null_value": 0, - "type": "long" - } - } - } - } - }, - "uptime-dynamic-settings": { - "properties": { - "heartbeatIndices": { - "type": "keyword" - } - } - }, - "url": { - "properties": { - "accessCount": { - "type": "long" - }, - "accessDate": { - "type": "date" - }, - "createDate": { - "type": "date" - }, - "url": { - "fields": { - "keyword": { - "ignore_above": 2048, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "visualization": { - "properties": { - "description": { - "type": "text" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "savedSearchRefName": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "type": "text" - }, - "version": { - "type": "integer" - }, - "visState": { - "type": "text" - } - } - } - } - }, - "settings": { - "index": { - "auto_expand_replicas": "0-1", - "number_of_replicas": "0", - "number_of_shards": "1" - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - ".siem-signals-default": { - "is_write_index": true - } - }, - "index": ".siem-signals-default-000001", - "mappings": { - "dynamic": "false", - "_meta": { - "version": 3 - }, - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "properties": { - "ephemeral_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "client": { - "properties": { - "address": { - "type": "keyword", - "ignore_above": 1024 - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "availability_zone": { - "type": "keyword", - "ignore_above": 1024 - }, - "instance": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "machine": { - "properties": { - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "provider": { - "type": "keyword", - "ignore_above": 1024 - }, - "region": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "container": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "image": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "tag": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "runtime": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "destination": { - "properties": { - "address": { - "type": "keyword", - "ignore_above": 1024 - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "dll": { - "properties": { - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024 - }, - "pe": { - "properties": { - "company": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "file_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "original_file_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "type": "keyword", - "ignore_above": 1024 - }, - "data": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "ttl": { - "type": "long" - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "header_flags": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "op_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "question": { - "properties": { - "class": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "subdomain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ecs": { - "properties": { - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "error": { - "properties": { - "code": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "message": { - "type": "text", - "norms": false - }, - "stack_trace": { - "type": "keyword", - "index": false, - "doc_values": false, - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "event": { - "properties": { - "action": { - "type": "keyword", - "ignore_above": 1024 - }, - "category": { - "type": "keyword", - "ignore_above": 1024 - }, - "code": { - "type": "keyword", - "ignore_above": 1024 - }, - "created": { - "type": "date" - }, - "dataset": { - "type": "keyword", - "ignore_above": 1024 - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "ingested": { - "type": "date" - }, - "kind": { - "type": "keyword", - "ignore_above": 1024 - }, - "module": { - "type": "keyword", - "ignore_above": 1024 - }, - "original": { - "type": "keyword", - "index": false, - "doc_values": false, - "ignore_above": 1024 - }, - "outcome": { - "type": "keyword", - "ignore_above": 1024 - }, - "provider": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "url": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "type": "keyword", - "ignore_above": 1024 - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "type": "keyword", - "ignore_above": 1024 - }, - "directory": { - "type": "keyword", - "ignore_above": 1024 - }, - "drive_letter": { - "type": "keyword", - "ignore_above": 1 - }, - "extension": { - "type": "keyword", - "ignore_above": 1024 - }, - "gid": { - "type": "keyword", - "ignore_above": 1024 - }, - "group": { - "type": "keyword", - "ignore_above": 1024 - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "inode": { - "type": "keyword", - "ignore_above": 1024 - }, - "mime_type": { - "type": "keyword", - "ignore_above": 1024 - }, - "mode": { - "type": "keyword", - "ignore_above": 1024 - }, - "mtime": { - "type": "date" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "owner": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "pe": { - "properties": { - "company": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "file_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "original_file_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "size": { - "type": "long" - }, - "target_path": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "uid": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "host": { - "properties": { - "architecture": { - "type": "keyword", - "ignore_above": 1024 - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hostname": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "os": { - "properties": { - "family": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "kernel": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "platform": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "http": { - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "method": { - "type": "keyword", - "ignore_above": 1024 - }, - "referrer": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "interface": { - "properties": { - "alias": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "labels": { - "type": "object" - }, - "log": { - "properties": { - "level": { - "type": "keyword", - "ignore_above": 1024 - }, - "logger": { - "type": "keyword", - "ignore_above": 1024 - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "integer" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "function": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "original": { - "type": "keyword", - "index": false, - "doc_values": false, - "ignore_above": 1024 - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - } - } - }, - "message": { - "type": "text", - "norms": false - }, - "network": { - "properties": { - "application": { - "type": "keyword", - "ignore_above": 1024 - }, - "bytes": { - "type": "long" - }, - "community_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "direction": { - "type": "keyword", - "ignore_above": 1024 - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "type": "keyword", - "ignore_above": 1024 - }, - "inner": { - "properties": { - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "packets": { - "type": "long" - }, - "protocol": { - "type": "keyword", - "ignore_above": 1024 - }, - "transport": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "observer": { - "properties": { - "egress": { - "properties": { - "interface": { - "properties": { - "alias": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "zone": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hostname": { - "type": "keyword", - "ignore_above": 1024 - }, - "ingress": { - "properties": { - "interface": { - "properties": { - "alias": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "zone": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "os": { - "properties": { - "family": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "kernel": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "platform": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - }, - "serial_number": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "vendor": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "organization": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "os": { - "properties": { - "family": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "kernel": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "platform": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "package": { - "properties": { - "architecture": { - "type": "keyword", - "ignore_above": 1024 - }, - "build_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "checksum": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "install_scope": { - "type": "keyword", - "ignore_above": 1024 - }, - "installed": { - "type": "date" - }, - "license": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - }, - "size": { - "type": "long" - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "pe": { - "properties": { - "company": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "file_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "original_file_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "process": { - "properties": { - "args": { - "type": "keyword", - "ignore_above": 1024 - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "entity_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "executable": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "parent": { - "properties": { - "args": { - "type": "keyword", - "ignore_above": 1024 - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "entity_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "executable": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "title": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "pe": { - "properties": { - "company": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "file_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "original_file_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "title": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "type": "keyword", - "ignore_above": 1024 - }, - "strings": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hive": { - "type": "keyword", - "ignore_above": 1024 - }, - "key": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024 - }, - "value": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "related": { - "properties": { - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "ip": { - "type": "ip" - }, - "user": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "rule": { - "properties": { - "author": { - "type": "keyword", - "ignore_above": 1024 - }, - "category": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "license": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - }, - "ruleset": { - "type": "keyword", - "ignore_above": 1024 - }, - "uuid": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "server": { - "properties": { - "address": { - "type": "keyword", - "ignore_above": 1024 - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "service": { - "properties": { - "ephemeral_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "node": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "state": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "signal": { - "properties": { - "ancestors": { - "properties": { - "depth": { - "type": "long" - }, - "id": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "rule": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "depth": { - "type": "integer" - }, - "group": { - "properties": { - "id": { - "type": "keyword" - }, - "index": { - "type": "integer" - } - } - }, - "original_event": { - "properties": { - "action": { - "type": "keyword" - }, - "category": { - "type": "keyword" - }, - "code": { - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "kind": { - "type": "keyword" - }, - "module": { - "type": "keyword" - }, - "original": { - "type": "keyword", - "index": false, - "doc_values": false - }, - "outcome": { - "type": "keyword" - }, - "provider": { - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "original_signal": { - "type": "object", - "dynamic": "false", - "enabled": false - }, - "original_time": { - "type": "date" - }, - "parent": { - "properties": { - "depth": { - "type": "long" - }, - "id": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "rule": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "parents": { - "properties": { - "depth": { - "type": "long" - }, - "id": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "rule": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "author": { - "type": "keyword" - }, - "building_block_type": { - "type": "keyword" - }, - "created_at": { - "type": "date" - }, - "created_by": { - "type": "keyword" - }, - "description": { - "type": "keyword" - }, - "enabled": { - "type": "keyword" - }, - "false_positives": { - "type": "keyword" - }, - "filters": { - "type": "object" - }, - "from": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "immutable": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "interval": { - "type": "keyword" - }, - "language": { - "type": "keyword" - }, - "license": { - "type": "keyword" - }, - "max_signals": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "note": { - "type": "text" - }, - "output_index": { - "type": "keyword" - }, - "query": { - "type": "keyword" - }, - "references": { - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_mapping": { - "properties": { - "field": { - "type": "keyword" - }, - "operator": { - "type": "keyword" - }, - "value": { - "type": "keyword" - } - } - }, - "rule_id": { - "type": "keyword" - }, - "rule_name_override": { - "type": "keyword" - }, - "saved_id": { - "type": "keyword" - }, - "severity": { - "type": "keyword" - }, - "severity_mapping": { - "properties": { - "field": { - "type": "keyword" - }, - "operator": { - "type": "keyword" - }, - "severity": { - "type": "keyword" - }, - "value": { - "type": "keyword" - } - } - }, - "size": { - "type": "keyword" - }, - "tags": { - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "reference": { - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "reference": { - "type": "keyword" - } - } - } - } - }, - "threshold": { - "properties": { - "field": { - "type": "keyword" - }, - "value": { - "type": "float" - } - } - }, - "timeline_id": { - "type": "keyword" - }, - "timeline_title": { - "type": "keyword" - }, - "timestamp_override": { - "type": "keyword" - }, - "to": { - "type": "keyword" - }, - "type": { - "type": "keyword" - }, - "updated_at": { - "type": "date" - }, - "updated_by": { - "type": "keyword" - }, - "version": { - "type": "keyword" - } - } - }, - "status": { - "type": "keyword" - }, - "threshold_count": { - "type": "float" - }, - "threshold_result": { - "properties": { - "count": { - "type": "long" - }, - "value": { - "type": "keyword" - } - } - } - } - }, - "source": { - "properties": { - "address": { - "type": "keyword", - "ignore_above": 1024 - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "tags": { - "type": "keyword", - "ignore_above": 1024 - }, - "threat": { - "properties": { - "framework": { - "type": "keyword", - "ignore_above": 1024 - }, - "tactic": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "technique": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "tls": { - "properties": { - "cipher": { - "type": "keyword", - "ignore_above": 1024 - }, - "client": { - "properties": { - "certificate": { - "type": "keyword", - "ignore_above": 1024 - }, - "certificate_chain": { - "type": "keyword", - "ignore_above": 1024 - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "issuer": { - "type": "keyword", - "ignore_above": 1024 - }, - "ja3": { - "type": "keyword", - "ignore_above": 1024 - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject": { - "type": "keyword", - "ignore_above": 1024 - }, - "supported_ciphers": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "curve": { - "type": "keyword", - "ignore_above": 1024 - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "type": "keyword", - "ignore_above": 1024 - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "type": "keyword", - "ignore_above": 1024 - }, - "certificate_chain": { - "type": "keyword", - "ignore_above": 1024 - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "issuer": { - "type": "keyword", - "ignore_above": 1024 - }, - "ja3s": { - "type": "keyword", - "ignore_above": 1024 - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - }, - "version_protocol": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "trace": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "transaction": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "url": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "extension": { - "type": "keyword", - "ignore_above": 1024 - }, - "fragment": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "original": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "password": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024 - }, - "port": { - "type": "long" - }, - "query": { - "type": "keyword", - "ignore_above": 1024 - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "scheme": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "username": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "user_agent": { - "properties": { - "device": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "original": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "os": { - "properties": { - "family": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "kernel": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "platform": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "vulnerability": { - "properties": { - "category": { - "type": "keyword", - "ignore_above": 1024 - }, - "classification": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "enumeration": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - }, - "report_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "scanner": { - "properties": { - "vendor": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "severity": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "settings": { - "index": { - "lifecycle": { - "name": ".siem-signals-default", - "rollover_alias": ".siem-signals-default" - }, - "number_of_replicas": "1", - "number_of_shards": "1" - } - } - } -} diff --git a/x-pack/test/security_solution_cypress/es_archives/prebuilt_rules_loaded/data.json.gz b/x-pack/test/security_solution_cypress/es_archives/prebuilt_rules_loaded/data.json.gz deleted file mode 100644 index 0bec997503146..0000000000000 Binary files a/x-pack/test/security_solution_cypress/es_archives/prebuilt_rules_loaded/data.json.gz and /dev/null differ diff --git a/x-pack/test/security_solution_cypress/es_archives/prebuilt_rules_loaded/mappings.json b/x-pack/test/security_solution_cypress/es_archives/prebuilt_rules_loaded/mappings.json deleted file mode 100644 index 7ef00495390ee..0000000000000 --- a/x-pack/test/security_solution_cypress/es_archives/prebuilt_rules_loaded/mappings.json +++ /dev/null @@ -1,2967 +0,0 @@ -{ - "type": "index", - "value": { - "aliases": { - ".kibana": { - } - }, - "index": ".kibana_1", - "mappings": { - "_meta": { - "migrationMappingPropertyHashes": { - "action": "6e96ac5e648f57523879661ea72525b7", - "action_task_params": "a9d49f184ee89641044be0ca2950fa3a", - "agent_actions": "ed270b46812f0fa1439366c428a2cf17", - "agent_configs": "38abaf89513877745c359e7700c0c66a", - "agent_events": "3231653fafe4ef3196fe3b32ab774bf2", - "agents": "c3eeb7b9d97176f15f6d126370ab23c7", - "alert": "7b44fba6773e37c806ce290ea9b7024e", - "apm-indices": "9bb9b2bf1fa636ed8619cbab5ce6a1dd", - "apm-telemetry": "3525d7c22c42bc80f5e6e9cb3f2b26a2", - "application_usage_totals": "c897e4310c5f24b07caaff3db53ae2c1", - "application_usage_transactional": "965839e75f809fefe04f92dc4d99722a", - "canvas-element": "7390014e1091044523666d97247392fc", - "canvas-workpad": "b0a1706d356228dbdcb4a17e6b9eb231", - "cases": "08b8b110dbca273d37e8aef131ecab61", - "cases-comments": "c2061fb929f585df57425102fa928b4b", - "cases-configure": "42711cbb311976c0687853f4c1354572", - "cases-user-actions": "32277330ec6b721abe3b846cfd939a71", - "config": "ae24d22d5986d04124cc6568f771066f", - "dashboard": "d00f614b29a80360e1190193fd333bab", - "datasources": "d4bc0c252b2b5683ff21ea32d00acffc", - "enrollment_api_keys": "28b91e20b105b6f928e2012600085d8f", - "epm-package": "0be91c6758421dd5d0f1a58e9e5bc7c3", - "file-upload-telemetry": "0ed4d3e1983d1217a30982630897092e", - "graph-workspace": "cd7ba1330e6682e9cc00b78850874be1", - "index-pattern": "66eccb05066c5a89924f48a9e9736499", - "infrastructure-ui-source": "ddc0ecb18383f6b26101a2fadb2dab0c", - "inventory-view": "9ecce5b58867403613d82fe496470b34", - "kql-telemetry": "d12a98a6f19a2d273696597547e064ee", - "lens": "21c3ea0763beb1ecb0162529706b88c5", - "lens-ui-telemetry": "509bfa5978586998e05f9e303c07a327", - "map": "23d7aa4a720d4938ccde3983f87bd58d", - "maps-telemetry": "268da3a48066123fc5baf35abaa55014", - "metrics-explorer-view": "53c5365793677328df0ccb6138bf3cdd", - "migrationVersion": "4a1746014a75ade3a714e1db5763276f", - "ml-telemetry": "257fd1d4b4fdbb9cb4b8a3b27da201e9", - "namespace": "2f4316de49999235636386fe51dc06c1", - "outputs": "aee9782e0d500b867859650a36280165", - "query": "11aaeb7f5f7fa5bb43f25e18ce26e7d9", - "references": "7997cf5a56cc02bdc9c93361bde732b0", - "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", - "search": "181661168bbadd1eff5902361e2a0d5c", - "server": "ec97f1c5da1a19609a60874e5af1100c", - "siem-detection-engine-rule-actions": "90eee2e4635260f4be0a1da8f5bc0aa0", - "siem-detection-engine-rule-status": "ae783f41c6937db6b7a2ef5c93a9e9b0", - "siem-ui-timeline": "ac8020190f5950dd3250b6499144e7fb", - "siem-ui-timeline-note": "8874706eedc49059d4cf0f5094559084", - "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", - "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", - "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", - "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", - "type": "2f4316de49999235636386fe51dc06c1", - "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", - "updated_at": "00da57df13e94e9d98437d13ace4bfe0", - "upgrade-assistant-reindex-operation": "a53a20fe086b72c9a86da3cc12dad8a6", - "upgrade-assistant-telemetry": "56702cec857e0a9dacfb696655b4ff7b", - "uptime-dynamic-settings": "b6289473c8985c79b6c47eebc19a0ca5", - "url": "c7f66a0df8b1b52f17c28c4adb111105", - "visualization": "52d7a13ad68a150c4525b292d23e12cc" - } - }, - "dynamic": "strict", - "properties": { - "action": { - "properties": { - "actionTypeId": { - "type": "keyword" - }, - "config": { - "enabled": false, - "type": "object" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - }, - "secrets": { - "type": "binary" - } - } - }, - "action_task_params": { - "properties": { - "actionId": { - "type": "keyword" - }, - "apiKey": { - "type": "binary" - }, - "params": { - "enabled": false, - "type": "object" - } - } - }, - "agent_actions": { - "properties": { - "agent_id": { - "type": "keyword" - }, - "created_at": { - "type": "date" - }, - "data": { - "type": "flattened" - }, - "sent_at": { - "type": "date" - }, - "type": { - "type": "keyword" - } - } - }, - "agent_configs": { - "properties": { - "datasources": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "id": { - "type": "keyword" - }, - "is_default": { - "type": "boolean" - }, - "name": { - "type": "text" - }, - "namespace": { - "type": "keyword" - }, - "revision": { - "type": "integer" - }, - "status": { - "type": "keyword" - }, - "updated_by": { - "type": "keyword" - }, - "updated_on": { - "type": "keyword" - } - } - }, - "agent_events": { - "properties": { - "action_id": { - "type": "keyword" - }, - "agent_id": { - "type": "keyword" - }, - "config_id": { - "type": "keyword" - }, - "data": { - "type": "text" - }, - "message": { - "type": "text" - }, - "payload": { - "type": "text" - }, - "stream_id": { - "type": "keyword" - }, - "subtype": { - "type": "keyword" - }, - "timestamp": { - "type": "date" - }, - "type": { - "type": "keyword" - } - } - }, - "agents": { - "properties": { - "access_api_key_id": { - "type": "keyword" - }, - "active": { - "type": "boolean" - }, - "config_id": { - "type": "keyword" - }, - "config_newest_revision": { - "type": "integer" - }, - "config_revision": { - "type": "integer" - }, - "current_error_events": { - "type": "text" - }, - "default_api_key": { - "type": "keyword" - }, - "enrolled_at": { - "type": "date" - }, - "last_checkin": { - "type": "date" - }, - "last_updated": { - "type": "date" - }, - "local_metadata": { - "type": "text" - }, - "shared_id": { - "type": "keyword" - }, - "type": { - "type": "keyword" - }, - "updated_at": { - "type": "date" - }, - "user_provided_metadata": { - "type": "text" - }, - "version": { - "type": "keyword" - } - } - }, - "alert": { - "properties": { - "actions": { - "properties": { - "actionRef": { - "type": "keyword" - }, - "actionTypeId": { - "type": "keyword" - }, - "group": { - "type": "keyword" - }, - "params": { - "enabled": false, - "type": "object" - } - }, - "type": "nested" - }, - "alertTypeId": { - "type": "keyword" - }, - "apiKey": { - "type": "binary" - }, - "apiKeyOwner": { - "type": "keyword" - }, - "consumer": { - "type": "keyword" - }, - "createdAt": { - "type": "date" - }, - "createdBy": { - "type": "keyword" - }, - "enabled": { - "type": "boolean" - }, - "muteAll": { - "type": "boolean" - }, - "mutedInstanceIds": { - "type": "keyword" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - }, - "params": { - "enabled": false, - "type": "object" - }, - "schedule": { - "properties": { - "interval": { - "type": "keyword" - } - } - }, - "scheduledTaskId": { - "type": "keyword" - }, - "tags": { - "type": "keyword" - }, - "throttle": { - "type": "keyword" - }, - "updatedAt": { - "type": "date" - }, - "updatedBy": { - "type": "keyword" - } - } - }, - "apm-indices": { - "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } - } - } - }, - "apm-telemetry": { - "properties": { - "agents": { - "properties": { - "dotnet": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "go": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "java": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "js-base": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "nodejs": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "python": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "ruby": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "rum-js": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - } - } - }, - "cardinality": { - "properties": { - "transaction": { - "properties": { - "name": { - "properties": { - "all_agents": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "rum": { - "properties": { - "1d": { - "type": "long" - } - } - } - } - } - } - }, - "user_agent": { - "properties": { - "original": { - "properties": { - "all_agents": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "rum": { - "properties": { - "1d": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "counts": { - "properties": { - "agent_configuration": { - "properties": { - "all": { - "type": "long" - } - } - }, - "error": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "max_error_groups_per_service": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "max_transaction_groups_per_service": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "metric": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "onboarding": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "services": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "sourcemap": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "span": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "traces": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "transaction": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - } - } - }, - "has_any_services": { - "type": "boolean" - }, - "indices": { - "properties": { - "all": { - "properties": { - "total": { - "properties": { - "docs": { - "properties": { - "count": { - "type": "long" - } - } - }, - "store": { - "properties": { - "size_in_bytes": { - "type": "long" - } - } - } - } - } - } - }, - "shards": { - "properties": { - "total": { - "type": "long" - } - } - } - } - }, - "integrations": { - "properties": { - "ml": { - "properties": { - "all_jobs_count": { - "type": "long" - } - } - } - } - }, - "retainment": { - "properties": { - "error": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "metric": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "onboarding": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "span": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "transaction": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "services_per_agent": { - "properties": { - "dotnet": { - "null_value": 0, - "type": "long" - }, - "go": { - "null_value": 0, - "type": "long" - }, - "java": { - "null_value": 0, - "type": "long" - }, - "js-base": { - "null_value": 0, - "type": "long" - }, - "nodejs": { - "null_value": 0, - "type": "long" - }, - "python": { - "null_value": 0, - "type": "long" - }, - "ruby": { - "null_value": 0, - "type": "long" - }, - "rum-js": { - "null_value": 0, - "type": "long" - } - } - }, - "tasks": { - "properties": { - "agent_configuration": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "agents": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "cardinality": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "groupings": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "indices_stats": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "integrations": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "processor_events": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "services": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "versions": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "version": { - "properties": { - "apm_server": { - "properties": { - "major": { - "type": "long" - }, - "minor": { - "type": "long" - }, - "patch": { - "type": "long" - } - } - } - } - } - } - }, - "application_usage_totals": { - "properties": { - "appId": { - "type": "keyword" - }, - "minutesOnScreen": { - "type": "float" - }, - "numberOfClicks": { - "type": "long" - } - } - }, - "application_usage_transactional": { - "properties": { - "appId": { - "type": "keyword" - }, - "minutesOnScreen": { - "type": "float" - }, - "numberOfClicks": { - "type": "long" - }, - "timestamp": { - "type": "date" - } - } - }, - "canvas-element": { - "dynamic": "false", - "properties": { - "@created": { - "type": "date" - }, - "@timestamp": { - "type": "date" - }, - "content": { - "type": "text" - }, - "help": { - "type": "text" - }, - "image": { - "type": "text" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "canvas-workpad": { - "dynamic": "false", - "properties": { - "@created": { - "type": "date" - }, - "@timestamp": { - "type": "date" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "cases": { - "properties": { - "closed_at": { - "type": "date" - }, - "closed_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "created_at": { - "type": "date" - }, - "created_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "description": { - "type": "text" - }, - "external_service": { - "properties": { - "connector_id": { - "type": "keyword" - }, - "connector_name": { - "type": "keyword" - }, - "external_id": { - "type": "keyword" - }, - "external_title": { - "type": "text" - }, - "external_url": { - "type": "text" - }, - "pushed_at": { - "type": "date" - }, - "pushed_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - } - } - }, - "status": { - "type": "keyword" - }, - "tags": { - "type": "keyword" - }, - "title": { - "type": "keyword" - }, - "updated_at": { - "type": "date" - }, - "updated_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - } - } - }, - "cases-comments": { - "properties": { - "comment": { - "type": "text" - }, - "created_at": { - "type": "date" - }, - "created_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "pushed_at": { - "type": "date" - }, - "pushed_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "updated_at": { - "type": "date" - }, - "updated_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - } - } - }, - "cases-configure": { - "properties": { - "closure_type": { - "type": "keyword" - }, - "connector_id": { - "type": "keyword" - }, - "connector_name": { - "type": "keyword" - }, - "created_at": { - "type": "date" - }, - "created_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "updated_at": { - "type": "date" - }, - "updated_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - } - } - }, - "cases-user-actions": { - "properties": { - "action": { - "type": "keyword" - }, - "action_at": { - "type": "date" - }, - "action_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "action_field": { - "type": "keyword" - }, - "new_value": { - "type": "text" - }, - "old_value": { - "type": "text" - } - } - }, - "config": { - "dynamic": "true", - "properties": { - "buildNum": { - "type": "keyword" - } - } - }, - "dashboard": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "optionsJSON": { - "type": "text" - }, - "panelsJSON": { - "type": "text" - }, - "refreshInterval": { - "properties": { - "display": { - "type": "keyword" - }, - "pause": { - "type": "boolean" - }, - "section": { - "type": "integer" - }, - "value": { - "type": "integer" - } - } - }, - "timeFrom": { - "type": "keyword" - }, - "timeRestore": { - "type": "boolean" - }, - "timeTo": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "datasources": { - "properties": { - "config_id": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "enabled": { - "type": "boolean" - }, - "inputs": { - "properties": { - "config": { - "type": "flattened" - }, - "enabled": { - "type": "boolean" - }, - "processors": { - "type": "keyword" - }, - "streams": { - "properties": { - "config": { - "type": "flattened" - }, - "dataset": { - "type": "keyword" - }, - "enabled": { - "type": "boolean" - }, - "id": { - "type": "keyword" - }, - "processors": { - "type": "keyword" - } - }, - "type": "nested" - }, - "type": { - "type": "keyword" - } - }, - "type": "nested" - }, - "name": { - "type": "keyword" - }, - "namespace": { - "type": "keyword" - }, - "output_id": { - "type": "keyword" - }, - "package": { - "properties": { - "name": { - "type": "keyword" - }, - "title": { - "type": "keyword" - }, - "version": { - "type": "keyword" - } - } - }, - "revision": { - "type": "integer" - } - } - }, - "enrollment_api_keys": { - "properties": { - "active": { - "type": "boolean" - }, - "api_key": { - "type": "binary" - }, - "api_key_id": { - "type": "keyword" - }, - "config_id": { - "type": "keyword" - }, - "created_at": { - "type": "date" - }, - "expire_at": { - "type": "date" - }, - "name": { - "type": "keyword" - }, - "type": { - "type": "keyword" - }, - "updated_at": { - "type": "date" - } - } - }, - "epm-package": { - "properties": { - "installed": { - "properties": { - "id": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - }, - "type": "nested" - }, - "internal": { - "type": "boolean" - }, - "name": { - "type": "keyword" - }, - "version": { - "type": "keyword" - } - } - }, - "file-upload-telemetry": { - "properties": { - "filesUploadedTotalCount": { - "type": "long" - } - } - }, - "graph-workspace": { - "properties": { - "description": { - "type": "text" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "numLinks": { - "type": "integer" - }, - "numVertices": { - "type": "integer" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - }, - "wsState": { - "type": "text" - } - } - }, - "index-pattern": { - "properties": { - "fieldFormatMap": { - "type": "text" - }, - "fields": { - "type": "text" - }, - "intervalName": { - "type": "keyword" - }, - "notExpandable": { - "type": "boolean" - }, - "sourceFilters": { - "type": "text" - }, - "timeFieldName": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "type": { - "type": "keyword" - }, - "typeMeta": { - "type": "keyword" - } - } - }, - "infrastructure-ui-source": { - "properties": { - "description": { - "type": "text" - }, - "fields": { - "properties": { - "container": { - "type": "keyword" - }, - "host": { - "type": "keyword" - }, - "pod": { - "type": "keyword" - }, - "tiebreaker": { - "type": "keyword" - }, - "timestamp": { - "type": "keyword" - } - } - }, - "logAlias": { - "type": "keyword" - }, - "logColumns": { - "properties": { - "fieldColumn": { - "properties": { - "field": { - "type": "keyword" - }, - "id": { - "type": "keyword" - } - } - }, - "messageColumn": { - "properties": { - "id": { - "type": "keyword" - } - } - }, - "timestampColumn": { - "properties": { - "id": { - "type": "keyword" - } - } - } - }, - "type": "nested" - }, - "metricAlias": { - "type": "keyword" - }, - "name": { - "type": "text" - } - } - }, - "inventory-view": { - "properties": { - "autoBounds": { - "type": "boolean" - }, - "autoReload": { - "type": "boolean" - }, - "boundsOverride": { - "properties": { - "max": { - "type": "integer" - }, - "min": { - "type": "integer" - } - } - }, - "customMetrics": { - "properties": { - "aggregation": { - "type": "keyword" - }, - "field": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "label": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - }, - "type": "nested" - }, - "customOptions": { - "properties": { - "field": { - "type": "keyword" - }, - "text": { - "type": "keyword" - } - }, - "type": "nested" - }, - "filterQuery": { - "properties": { - "expression": { - "type": "keyword" - }, - "kind": { - "type": "keyword" - } - } - }, - "groupBy": { - "properties": { - "field": { - "type": "keyword" - }, - "label": { - "type": "keyword" - } - }, - "type": "nested" - }, - "metric": { - "properties": { - "aggregation": { - "type": "keyword" - }, - "field": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "label": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "name": { - "type": "keyword" - }, - "nodeType": { - "type": "keyword" - }, - "time": { - "type": "integer" - }, - "view": { - "type": "keyword" - } - } - }, - "kql-telemetry": { - "properties": { - "optInCount": { - "type": "long" - }, - "optOutCount": { - "type": "long" - } - } - }, - "lens": { - "properties": { - "expression": { - "index": false, - "type": "keyword" - }, - "state": { - "type": "flattened" - }, - "title": { - "type": "text" - }, - "visualizationType": { - "type": "keyword" - } - } - }, - "lens-ui-telemetry": { - "properties": { - "count": { - "type": "integer" - }, - "date": { - "type": "date" - }, - "name": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "map": { - "properties": { - "bounds": { - "type": "geo_shape" - }, - "description": { - "type": "text" - }, - "layerListJSON": { - "type": "text" - }, - "mapStateJSON": { - "type": "text" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "maps-telemetry": { - "properties": { - "attributesPerMap": { - "properties": { - "dataSourcesCount": { - "properties": { - "avg": { - "type": "long" - }, - "max": { - "type": "long" - }, - "min": { - "type": "long" - } - } - }, - "emsVectorLayersCount": { - "dynamic": "true", - "type": "object" - }, - "layerTypesCount": { - "dynamic": "true", - "type": "object" - }, - "layersCount": { - "properties": { - "avg": { - "type": "long" - }, - "max": { - "type": "long" - }, - "min": { - "type": "long" - } - } - } - } - }, - "indexPatternsWithGeoFieldCount": { - "type": "long" - }, - "mapsTotalCount": { - "type": "long" - }, - "settings": { - "properties": { - "showMapVisualizationTypes": { - "type": "boolean" - } - } - }, - "timeCaptured": { - "type": "date" - } - } - }, - "metrics-explorer-view": { - "properties": { - "chartOptions": { - "properties": { - "stack": { - "type": "boolean" - }, - "type": { - "type": "keyword" - }, - "yAxisMode": { - "type": "keyword" - } - } - }, - "currentTimerange": { - "properties": { - "from": { - "type": "keyword" - }, - "interval": { - "type": "keyword" - }, - "to": { - "type": "keyword" - } - } - }, - "name": { - "type": "keyword" - }, - "options": { - "properties": { - "aggregation": { - "type": "keyword" - }, - "filterQuery": { - "type": "keyword" - }, - "groupBy": { - "type": "keyword" - }, - "limit": { - "type": "integer" - }, - "metrics": { - "properties": { - "aggregation": { - "type": "keyword" - }, - "color": { - "type": "keyword" - }, - "field": { - "type": "keyword" - }, - "label": { - "type": "keyword" - } - }, - "type": "nested" - } - } - } - } - }, - "migrationVersion": { - "dynamic": "true", - "properties": { - "space": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "ml-telemetry": { - "properties": { - "file_data_visualizer": { - "properties": { - "index_creation_count": { - "type": "long" - } - } - } - } - }, - "namespace": { - "type": "keyword" - }, - "outputs": { - "properties": { - "api_key": { - "type": "keyword" - }, - "ca_sha256": { - "type": "keyword" - }, - "config": { - "type": "flattened" - }, - "fleet_enroll_password": { - "type": "binary" - }, - "fleet_enroll_username": { - "type": "binary" - }, - "hosts": { - "type": "keyword" - }, - "is_default": { - "type": "boolean" - }, - "name": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "query": { - "properties": { - "description": { - "type": "text" - }, - "filters": { - "enabled": false, - "type": "object" - }, - "query": { - "properties": { - "language": { - "type": "keyword" - }, - "query": { - "index": false, - "type": "keyword" - } - } - }, - "timefilter": { - "enabled": false, - "type": "object" - }, - "title": { - "type": "text" - } - } - }, - "references": { - "properties": { - "id": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - }, - "type": "nested" - }, - "sample-data-telemetry": { - "properties": { - "installCount": { - "type": "long" - }, - "unInstallCount": { - "type": "long" - } - } - }, - "search": { - "properties": { - "columns": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "sort": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "server": { - "properties": { - "uuid": { - "type": "keyword" - } - } - }, - "siem-detection-engine-rule-actions": { - "properties": { - "actions": { - "properties": { - "action_type_id": { - "type": "keyword" - }, - "group": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "params": { - "dynamic": "true", - "type": "object" - } - } - }, - "alertThrottle": { - "type": "keyword" - }, - "ruleAlertId": { - "type": "keyword" - }, - "ruleThrottle": { - "type": "keyword" - } - } - }, - "siem-detection-engine-rule-status": { - "properties": { - "alertId": { - "type": "keyword" - }, - "bulkCreateTimeDurations": { - "type": "float" - }, - "gap": { - "type": "text" - }, - "lastFailureAt": { - "type": "date" - }, - "lastFailureMessage": { - "type": "text" - }, - "lastLookBackDate": { - "type": "date" - }, - "lastSuccessAt": { - "type": "date" - }, - "lastSuccessMessage": { - "type": "text" - }, - "searchAfterTimeDurations": { - "type": "float" - }, - "status": { - "type": "keyword" - }, - "statusDate": { - "type": "date" - } - } - }, - "siem-ui-timeline": { - "properties": { - "columns": { - "properties": { - "aggregatable": { - "type": "boolean" - }, - "category": { - "type": "keyword" - }, - "columnHeaderType": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "example": { - "type": "text" - }, - "id": { - "type": "keyword" - }, - "indexes": { - "type": "keyword" - }, - "name": { - "type": "text" - }, - "placeholder": { - "type": "text" - }, - "searchable": { - "type": "boolean" - }, - "type": { - "type": "keyword" - } - } - }, - "created": { - "type": "date" - }, - "createdBy": { - "type": "text" - }, - "dataProviders": { - "properties": { - "and": { - "properties": { - "enabled": { - "type": "boolean" - }, - "excluded": { - "type": "boolean" - }, - "id": { - "type": "keyword" - }, - "kqlQuery": { - "type": "text" - }, - "name": { - "type": "text" - }, - "queryMatch": { - "properties": { - "displayField": { - "type": "text" - }, - "displayValue": { - "type": "text" - }, - "field": { - "type": "text" - }, - "operator": { - "type": "text" - }, - "value": { - "type": "text" - } - } - } - } - }, - "enabled": { - "type": "boolean" - }, - "excluded": { - "type": "boolean" - }, - "id": { - "type": "keyword" - }, - "kqlQuery": { - "type": "text" - }, - "name": { - "type": "text" - }, - "queryMatch": { - "properties": { - "displayField": { - "type": "text" - }, - "displayValue": { - "type": "text" - }, - "field": { - "type": "text" - }, - "operator": { - "type": "text" - }, - "value": { - "type": "text" - } - } - } - } - }, - "dateRange": { - "properties": { - "end": { - "type": "date" - }, - "start": { - "type": "date" - } - } - }, - "description": { - "type": "text" - }, - "eventType": { - "type": "keyword" - }, - "favorite": { - "properties": { - "favoriteDate": { - "type": "date" - }, - "fullName": { - "type": "text" - }, - "keySearch": { - "type": "text" - }, - "userName": { - "type": "text" - } - } - }, - "filters": { - "properties": { - "exists": { - "type": "text" - }, - "match_all": { - "type": "text" - }, - "meta": { - "properties": { - "alias": { - "type": "text" - }, - "controlledBy": { - "type": "text" - }, - "disabled": { - "type": "boolean" - }, - "field": { - "type": "text" - }, - "formattedValue": { - "type": "text" - }, - "index": { - "type": "keyword" - }, - "key": { - "type": "keyword" - }, - "negate": { - "type": "boolean" - }, - "params": { - "type": "text" - }, - "type": { - "type": "keyword" - }, - "value": { - "type": "text" - } - } - }, - "missing": { - "type": "text" - }, - "query": { - "type": "text" - }, - "range": { - "type": "text" - }, - "script": { - "type": "text" - } - } - }, - "kqlMode": { - "type": "keyword" - }, - "kqlQuery": { - "properties": { - "filterQuery": { - "properties": { - "kuery": { - "properties": { - "expression": { - "type": "text" - }, - "kind": { - "type": "keyword" - } - } - }, - "serializedQuery": { - "type": "text" - } - } - } - } - }, - "savedQueryId": { - "type": "keyword" - }, - "sort": { - "properties": { - "columnId": { - "type": "keyword" - }, - "sortDirection": { - "type": "keyword" - } - } - }, - "title": { - "type": "text" - }, - "updated": { - "type": "date" - }, - "updatedBy": { - "type": "text" - } - } - }, - "siem-ui-timeline-note": { - "properties": { - "created": { - "type": "date" - }, - "createdBy": { - "type": "text" - }, - "eventId": { - "type": "keyword" - }, - "note": { - "type": "text" - }, - "timelineId": { - "type": "keyword" - }, - "updated": { - "type": "date" - }, - "updatedBy": { - "type": "text" - } - } - }, - "siem-ui-timeline-pinned-event": { - "properties": { - "created": { - "type": "date" - }, - "createdBy": { - "type": "text" - }, - "eventId": { - "type": "keyword" - }, - "timelineId": { - "type": "keyword" - }, - "updated": { - "type": "date" - }, - "updatedBy": { - "type": "text" - } - } - }, - "space": { - "properties": { - "_reserved": { - "type": "boolean" - }, - "color": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "disabledFeatures": { - "type": "keyword" - }, - "imageUrl": { - "index": false, - "type": "text" - }, - "initials": { - "type": "keyword" - }, - "name": { - "fields": { - "keyword": { - "ignore_above": 2048, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "telemetry": { - "properties": { - "allowChangingOptInStatus": { - "type": "boolean" - }, - "enabled": { - "type": "boolean" - }, - "lastReported": { - "type": "date" - }, - "lastVersionChecked": { - "type": "keyword" - }, - "reportFailureCount": { - "type": "integer" - }, - "reportFailureVersion": { - "type": "keyword" - }, - "sendUsageFrom": { - "type": "keyword" - }, - "userHasSeenNotice": { - "type": "boolean" - } - } - }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "tsvb-validation-telemetry": { - "properties": { - "failedRequests": { - "type": "long" - } - } - }, - "type": { - "type": "keyword" - }, - "ui-metric": { - "properties": { - "count": { - "type": "integer" - } - } - }, - "updated_at": { - "type": "date" - }, - "upgrade-assistant-reindex-operation": { - "dynamic": "true", - "properties": { - "indexName": { - "type": "keyword" - }, - "status": { - "type": "integer" - } - } - }, - "upgrade-assistant-telemetry": { - "properties": { - "features": { - "properties": { - "deprecation_logging": { - "properties": { - "enabled": { - "null_value": true, - "type": "boolean" - } - } - } - } - }, - "ui_open": { - "properties": { - "cluster": { - "null_value": 0, - "type": "long" - }, - "indices": { - "null_value": 0, - "type": "long" - }, - "overview": { - "null_value": 0, - "type": "long" - } - } - }, - "ui_reindex": { - "properties": { - "close": { - "null_value": 0, - "type": "long" - }, - "open": { - "null_value": 0, - "type": "long" - }, - "start": { - "null_value": 0, - "type": "long" - }, - "stop": { - "null_value": 0, - "type": "long" - } - } - } - } - }, - "uptime-dynamic-settings": { - "properties": { - "heartbeatIndices": { - "type": "keyword" - } - } - }, - "url": { - "properties": { - "accessCount": { - "type": "long" - }, - "accessDate": { - "type": "date" - }, - "createDate": { - "type": "date" - }, - "url": { - "fields": { - "keyword": { - "ignore_above": 2048, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "visualization": { - "properties": { - "description": { - "type": "text" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "savedSearchRefName": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "type": "text" - }, - "version": { - "type": "integer" - }, - "visState": { - "type": "text" - } - } - } - } - }, - "settings": { - "index": { - "auto_expand_replicas": "0-1", - "number_of_replicas": "0", - "number_of_shards": "1" - } - } - } -} \ No newline at end of file diff --git a/x-pack/test/security_solution_cypress/es_archives/timeline_alerts/data.json.gz b/x-pack/test/security_solution_cypress/es_archives/timeline_alerts/data.json.gz deleted file mode 100644 index 485d9868efd21..0000000000000 Binary files a/x-pack/test/security_solution_cypress/es_archives/timeline_alerts/data.json.gz and /dev/null differ diff --git a/x-pack/test/security_solution_cypress/es_archives/timeline_alerts/mappings.json b/x-pack/test/security_solution_cypress/es_archives/timeline_alerts/mappings.json deleted file mode 100644 index 4e5683f2f3932..0000000000000 --- a/x-pack/test/security_solution_cypress/es_archives/timeline_alerts/mappings.json +++ /dev/null @@ -1,9588 +0,0 @@ -{ - "type": "index", - "value": { - "aliases": { - ".kibana": { - } - }, - "index": ".kibana_1", - "mappings": { - "_meta": { - "migrationMappingPropertyHashes": { - "action": "c0c235fba02ebd2a2412bcda79009b58", - "action_task_params": "a9d49f184ee89641044be0ca2950fa3a", - "alert": "e588043a01d3d43477e7cad7efa0f5d8", - "apm-indices": "9bb9b2bf1fa636ed8619cbab5ce6a1dd", - "apm-services-telemetry": "07ee1939fa4302c62ddc052ec03fed90", - "canvas-element": "7390014e1091044523666d97247392fc", - "canvas-workpad": "b0a1706d356228dbdcb4a17e6b9eb231", - "config": "87aca8fdb053154f11383fce3dbf3edf", - "dashboard": "d00f614b29a80360e1190193fd333bab", - "file-upload-telemetry": "0ed4d3e1983d1217a30982630897092e", - "graph-workspace": "cd7ba1330e6682e9cc00b78850874be1", - "index-pattern": "66eccb05066c5a89924f48a9e9736499", - "infrastructure-ui-source": "ddc0ecb18383f6b26101a2fadb2dab0c", - "inventory-view": "84b320fd67209906333ffce261128462", - "kql-telemetry": "d12a98a6f19a2d273696597547e064ee", - "lens": "21c3ea0763beb1ecb0162529706b88c5", - "lens-ui-telemetry": "509bfa5978586998e05f9e303c07a327", - "map": "23d7aa4a720d4938ccde3983f87bd58d", - "maps-telemetry": "268da3a48066123fc5baf35abaa55014", - "metrics-explorer-view": "53c5365793677328df0ccb6138bf3cdd", - "migrationVersion": "4a1746014a75ade3a714e1db5763276f", - "ml-telemetry": "257fd1d4b4fdbb9cb4b8a3b27da201e9", - "namespace": "2f4316de49999235636386fe51dc06c1", - "query": "11aaeb7f5f7fa5bb43f25e18ce26e7d9", - "references": "7997cf5a56cc02bdc9c93361bde732b0", - "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", - "search": "181661168bbadd1eff5902361e2a0d5c", - "server": "ec97f1c5da1a19609a60874e5af1100c", - "siem-detection-engine-rule-status": "0367e4d775814b56a4bee29384f9aafe", - "siem-ui-timeline": "ac8020190f5950dd3250b6499144e7fb", - "siem-ui-timeline-note": "8874706eedc49059d4cf0f5094559084", - "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", - "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", - "telemetry": "358ffaa88ba34a97d55af0933a117de4", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", - "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", - "type": "2f4316de49999235636386fe51dc06c1", - "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", - "updated_at": "00da57df13e94e9d98437d13ace4bfe0", - "upgrade-assistant-reindex-operation": "a53a20fe086b72c9a86da3cc12dad8a6", - "upgrade-assistant-telemetry": "56702cec857e0a9dacfb696655b4ff7b", - "url": "c7f66a0df8b1b52f17c28c4adb111105", - "visualization": "52d7a13ad68a150c4525b292d23e12cc" - } - }, - "dynamic": "strict", - "properties": { - "action": { - "properties": { - "actionTypeId": { - "type": "keyword" - }, - "config": { - "enabled": false, - "type": "object" - }, - "name": { - "type": "text" - }, - "secrets": { - "type": "binary" - } - } - }, - "action_task_params": { - "properties": { - "actionId": { - "type": "keyword" - }, - "apiKey": { - "type": "binary" - }, - "params": { - "enabled": false, - "type": "object" - } - } - }, - "alert": { - "properties": { - "actions": { - "properties": { - "actionRef": { - "type": "keyword" - }, - "actionTypeId": { - "type": "keyword" - }, - "group": { - "type": "keyword" - }, - "params": { - "enabled": false, - "type": "object" - } - }, - "type": "nested" - }, - "alertTypeId": { - "type": "keyword" - }, - "apiKey": { - "type": "binary" - }, - "apiKeyOwner": { - "type": "keyword" - }, - "consumer": { - "type": "keyword" - }, - "createdAt": { - "type": "date" - }, - "createdBy": { - "type": "keyword" - }, - "enabled": { - "type": "boolean" - }, - "muteAll": { - "type": "boolean" - }, - "mutedInstanceIds": { - "type": "keyword" - }, - "name": { - "type": "text" - }, - "params": { - "enabled": false, - "type": "object" - }, - "schedule": { - "properties": { - "interval": { - "type": "keyword" - } - } - }, - "scheduledTaskId": { - "type": "keyword" - }, - "tags": { - "type": "keyword" - }, - "throttle": { - "type": "keyword" - }, - "updatedAt": { - "type": "date" - }, - "updatedBy": { - "type": "keyword" - } - } - }, - "apm-indices": { - "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } - } - } - }, - "apm-services-telemetry": { - "properties": { - "has_any_services": { - "type": "boolean" - }, - "services_per_agent": { - "properties": { - "dotnet": { - "null_value": 0, - "type": "long" - }, - "go": { - "null_value": 0, - "type": "long" - }, - "java": { - "null_value": 0, - "type": "long" - }, - "js-base": { - "null_value": 0, - "type": "long" - }, - "nodejs": { - "null_value": 0, - "type": "long" - }, - "python": { - "null_value": 0, - "type": "long" - }, - "ruby": { - "null_value": 0, - "type": "long" - }, - "rum-js": { - "null_value": 0, - "type": "long" - } - } - } - } - }, - "canvas-element": { - "dynamic": "false", - "properties": { - "@created": { - "type": "date" - }, - "@timestamp": { - "type": "date" - }, - "content": { - "type": "text" - }, - "help": { - "type": "text" - }, - "image": { - "type": "text" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "canvas-workpad": { - "dynamic": "false", - "properties": { - "@created": { - "type": "date" - }, - "@timestamp": { - "type": "date" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "config": { - "dynamic": "true", - "properties": { - "buildNum": { - "type": "keyword" - } - } - }, - "dashboard": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "optionsJSON": { - "type": "text" - }, - "panelsJSON": { - "type": "text" - }, - "refreshInterval": { - "properties": { - "display": { - "type": "keyword" - }, - "pause": { - "type": "boolean" - }, - "section": { - "type": "integer" - }, - "value": { - "type": "integer" - } - } - }, - "timeFrom": { - "type": "keyword" - }, - "timeRestore": { - "type": "boolean" - }, - "timeTo": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "file-upload-telemetry": { - "properties": { - "filesUploadedTotalCount": { - "type": "long" - } - } - }, - "graph-workspace": { - "properties": { - "description": { - "type": "text" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "numLinks": { - "type": "integer" - }, - "numVertices": { - "type": "integer" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - }, - "wsState": { - "type": "text" - } - } - }, - "index-pattern": { - "properties": { - "fieldFormatMap": { - "type": "text" - }, - "fields": { - "type": "text" - }, - "intervalName": { - "type": "keyword" - }, - "notExpandable": { - "type": "boolean" - }, - "sourceFilters": { - "type": "text" - }, - "timeFieldName": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "type": { - "type": "keyword" - }, - "typeMeta": { - "type": "keyword" - } - } - }, - "infrastructure-ui-source": { - "properties": { - "description": { - "type": "text" - }, - "fields": { - "properties": { - "container": { - "type": "keyword" - }, - "host": { - "type": "keyword" - }, - "pod": { - "type": "keyword" - }, - "tiebreaker": { - "type": "keyword" - }, - "timestamp": { - "type": "keyword" - } - } - }, - "logAlias": { - "type": "keyword" - }, - "logColumns": { - "properties": { - "fieldColumn": { - "properties": { - "field": { - "type": "keyword" - }, - "id": { - "type": "keyword" - } - } - }, - "messageColumn": { - "properties": { - "id": { - "type": "keyword" - } - } - }, - "timestampColumn": { - "properties": { - "id": { - "type": "keyword" - } - } - } - }, - "type": "nested" - }, - "metricAlias": { - "type": "keyword" - }, - "name": { - "type": "text" - } - } - }, - "inventory-view": { - "properties": { - "autoBounds": { - "type": "boolean" - }, - "autoReload": { - "type": "boolean" - }, - "boundsOverride": { - "properties": { - "max": { - "type": "integer" - }, - "min": { - "type": "integer" - } - } - }, - "customOptions": { - "properties": { - "field": { - "type": "keyword" - }, - "text": { - "type": "keyword" - } - }, - "type": "nested" - }, - "filterQuery": { - "properties": { - "expression": { - "type": "keyword" - }, - "kind": { - "type": "keyword" - } - } - }, - "groupBy": { - "properties": { - "field": { - "type": "keyword" - }, - "label": { - "type": "keyword" - } - }, - "type": "nested" - }, - "metric": { - "properties": { - "type": { - "type": "keyword" - } - } - }, - "name": { - "type": "keyword" - }, - "nodeType": { - "type": "keyword" - }, - "time": { - "type": "integer" - }, - "view": { - "type": "keyword" - } - } - }, - "kql-telemetry": { - "properties": { - "optInCount": { - "type": "long" - }, - "optOutCount": { - "type": "long" - } - } - }, - "lens": { - "properties": { - "expression": { - "index": false, - "type": "keyword" - }, - "state": { - "type": "flattened" - }, - "title": { - "type": "text" - }, - "visualizationType": { - "type": "keyword" - } - } - }, - "lens-ui-telemetry": { - "properties": { - "count": { - "type": "integer" - }, - "date": { - "type": "date" - }, - "name": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "map": { - "properties": { - "bounds": { - "type": "geo_shape" - }, - "description": { - "type": "text" - }, - "layerListJSON": { - "type": "text" - }, - "mapStateJSON": { - "type": "text" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "maps-telemetry": { - "properties": { - "attributesPerMap": { - "properties": { - "dataSourcesCount": { - "properties": { - "avg": { - "type": "long" - }, - "max": { - "type": "long" - }, - "min": { - "type": "long" - } - } - }, - "emsVectorLayersCount": { - "dynamic": "true", - "type": "object" - }, - "layerTypesCount": { - "dynamic": "true", - "type": "object" - }, - "layersCount": { - "properties": { - "avg": { - "type": "long" - }, - "max": { - "type": "long" - }, - "min": { - "type": "long" - } - } - } - } - }, - "indexPatternsWithGeoFieldCount": { - "type": "long" - }, - "mapsTotalCount": { - "type": "long" - }, - "settings": { - "properties": { - "showMapVisualizationTypes": { - "type": "boolean" - } - } - }, - "timeCaptured": { - "type": "date" - } - } - }, - "metrics-explorer-view": { - "properties": { - "chartOptions": { - "properties": { - "stack": { - "type": "boolean" - }, - "type": { - "type": "keyword" - }, - "yAxisMode": { - "type": "keyword" - } - } - }, - "currentTimerange": { - "properties": { - "from": { - "type": "keyword" - }, - "interval": { - "type": "keyword" - }, - "to": { - "type": "keyword" - } - } - }, - "name": { - "type": "keyword" - }, - "options": { - "properties": { - "aggregation": { - "type": "keyword" - }, - "filterQuery": { - "type": "keyword" - }, - "groupBy": { - "type": "keyword" - }, - "limit": { - "type": "integer" - }, - "metrics": { - "properties": { - "aggregation": { - "type": "keyword" - }, - "color": { - "type": "keyword" - }, - "field": { - "type": "keyword" - }, - "label": { - "type": "keyword" - } - }, - "type": "nested" - } - } - } - } - }, - "migrationVersion": { - "dynamic": "true", - "properties": { - "dashboard": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, - "index-pattern": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, - "search": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, - "space": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, - "visualization": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "ml-telemetry": { - "properties": { - "file_data_visualizer": { - "properties": { - "index_creation_count": { - "type": "long" - } - } - } - } - }, - "namespace": { - "type": "keyword" - }, - "query": { - "properties": { - "description": { - "type": "text" - }, - "filters": { - "enabled": false, - "type": "object" - }, - "query": { - "properties": { - "language": { - "type": "keyword" - }, - "query": { - "index": false, - "type": "keyword" - } - } - }, - "timefilter": { - "enabled": false, - "type": "object" - }, - "title": { - "type": "text" - } - } - }, - "references": { - "properties": { - "id": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - }, - "type": "nested" - }, - "sample-data-telemetry": { - "properties": { - "installCount": { - "type": "long" - }, - "unInstallCount": { - "type": "long" - } - } - }, - "search": { - "properties": { - "columns": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "sort": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "server": { - "properties": { - "uuid": { - "type": "keyword" - } - } - }, - "siem-detection-engine-rule-status": { - "properties": { - "alertId": { - "type": "keyword" - }, - "lastFailureAt": { - "type": "date" - }, - "lastFailureMessage": { - "type": "text" - }, - "lastSuccessAt": { - "type": "date" - }, - "lastSuccessMessage": { - "type": "text" - }, - "status": { - "type": "keyword" - }, - "statusDate": { - "type": "date" - } - } - }, - "siem-ui-timeline": { - "properties": { - "columns": { - "properties": { - "aggregatable": { - "type": "boolean" - }, - "category": { - "type": "keyword" - }, - "columnHeaderType": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "example": { - "type": "text" - }, - "id": { - "type": "keyword" - }, - "indexes": { - "type": "keyword" - }, - "name": { - "type": "text" - }, - "placeholder": { - "type": "text" - }, - "searchable": { - "type": "boolean" - }, - "type": { - "type": "keyword" - } - } - }, - "created": { - "type": "date" - }, - "createdBy": { - "type": "text" - }, - "dataProviders": { - "properties": { - "and": { - "properties": { - "enabled": { - "type": "boolean" - }, - "excluded": { - "type": "boolean" - }, - "id": { - "type": "keyword" - }, - "kqlQuery": { - "type": "text" - }, - "name": { - "type": "text" - }, - "queryMatch": { - "properties": { - "displayField": { - "type": "text" - }, - "displayValue": { - "type": "text" - }, - "field": { - "type": "text" - }, - "operator": { - "type": "text" - }, - "value": { - "type": "text" - } - } - } - } - }, - "enabled": { - "type": "boolean" - }, - "excluded": { - "type": "boolean" - }, - "id": { - "type": "keyword" - }, - "kqlQuery": { - "type": "text" - }, - "name": { - "type": "text" - }, - "queryMatch": { - "properties": { - "displayField": { - "type": "text" - }, - "displayValue": { - "type": "text" - }, - "field": { - "type": "text" - }, - "operator": { - "type": "text" - }, - "value": { - "type": "text" - } - } - } - } - }, - "dateRange": { - "properties": { - "end": { - "type": "date" - }, - "start": { - "type": "date" - } - } - }, - "description": { - "type": "text" - }, - "eventType": { - "type": "keyword" - }, - "favorite": { - "properties": { - "favoriteDate": { - "type": "date" - }, - "fullName": { - "type": "text" - }, - "keySearch": { - "type": "text" - }, - "userName": { - "type": "text" - } - } - }, - "filters": { - "properties": { - "exists": { - "type": "text" - }, - "match_all": { - "type": "text" - }, - "meta": { - "properties": { - "alias": { - "type": "text" - }, - "controlledBy": { - "type": "text" - }, - "disabled": { - "type": "boolean" - }, - "field": { - "type": "text" - }, - "formattedValue": { - "type": "text" - }, - "index": { - "type": "keyword" - }, - "key": { - "type": "keyword" - }, - "negate": { - "type": "boolean" - }, - "params": { - "type": "text" - }, - "type": { - "type": "keyword" - }, - "value": { - "type": "text" - } - } - }, - "missing": { - "type": "text" - }, - "query": { - "type": "text" - }, - "range": { - "type": "text" - }, - "script": { - "type": "text" - } - } - }, - "kqlMode": { - "type": "keyword" - }, - "kqlQuery": { - "properties": { - "filterQuery": { - "properties": { - "kuery": { - "properties": { - "expression": { - "type": "text" - }, - "kind": { - "type": "keyword" - } - } - }, - "serializedQuery": { - "type": "text" - } - } - } - } - }, - "savedQueryId": { - "type": "keyword" - }, - "sort": { - "properties": { - "columnId": { - "type": "keyword" - }, - "sortDirection": { - "type": "keyword" - } - } - }, - "title": { - "type": "text" - }, - "updated": { - "type": "date" - }, - "updatedBy": { - "type": "text" - } - } - }, - "siem-ui-timeline-note": { - "properties": { - "created": { - "type": "date" - }, - "createdBy": { - "type": "text" - }, - "eventId": { - "type": "keyword" - }, - "note": { - "type": "text" - }, - "timelineId": { - "type": "keyword" - }, - "updated": { - "type": "date" - }, - "updatedBy": { - "type": "text" - } - } - }, - "siem-ui-timeline-pinned-event": { - "properties": { - "created": { - "type": "date" - }, - "createdBy": { - "type": "text" - }, - "eventId": { - "type": "keyword" - }, - "timelineId": { - "type": "keyword" - }, - "updated": { - "type": "date" - }, - "updatedBy": { - "type": "text" - } - } - }, - "space": { - "properties": { - "_reserved": { - "type": "boolean" - }, - "color": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "disabledFeatures": { - "type": "keyword" - }, - "imageUrl": { - "index": false, - "type": "text" - }, - "initials": { - "type": "keyword" - }, - "name": { - "fields": { - "keyword": { - "ignore_above": 2048, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "telemetry": { - "properties": { - "enabled": { - "type": "boolean" - }, - "lastReported": { - "type": "date" - }, - "lastVersionChecked": { - "ignore_above": 256, - "type": "keyword" - }, - "sendUsageFrom": { - "ignore_above": 256, - "type": "keyword" - }, - "userHasSeenNotice": { - "type": "boolean" - } - } - }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "tsvb-validation-telemetry": { - "properties": { - "failedRequests": { - "type": "long" - } - } - }, - "type": { - "type": "keyword" - }, - "ui-metric": { - "properties": { - "count": { - "type": "integer" - } - } - }, - "updated_at": { - "type": "date" - }, - "upgrade-assistant-reindex-operation": { - "dynamic": "true", - "properties": { - "indexName": { - "type": "keyword" - }, - "status": { - "type": "integer" - } - } - }, - "upgrade-assistant-telemetry": { - "properties": { - "features": { - "properties": { - "deprecation_logging": { - "properties": { - "enabled": { - "null_value": true, - "type": "boolean" - } - } - } - } - }, - "ui_open": { - "properties": { - "cluster": { - "null_value": 0, - "type": "long" - }, - "indices": { - "null_value": 0, - "type": "long" - }, - "overview": { - "null_value": 0, - "type": "long" - } - } - }, - "ui_reindex": { - "properties": { - "close": { - "null_value": 0, - "type": "long" - }, - "open": { - "null_value": 0, - "type": "long" - }, - "start": { - "null_value": 0, - "type": "long" - }, - "stop": { - "null_value": 0, - "type": "long" - } - } - } - } - }, - "url": { - "properties": { - "accessCount": { - "type": "long" - }, - "accessDate": { - "type": "date" - }, - "createDate": { - "type": "date" - }, - "url": { - "fields": { - "keyword": { - "ignore_above": 2048, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "visualization": { - "properties": { - "description": { - "type": "text" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "savedSearchRefName": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "type": "text" - }, - "version": { - "type": "integer" - }, - "visState": { - "type": "text" - } - } - } - } - }, - "settings": { - "index": { - "auto_expand_replicas": "0-1", - "number_of_replicas": "1", - "number_of_shards": "1" - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - ".siem-signals-default": { - "is_write_index": true - } - }, - "index": ".siem-signals-default-000001", - "mappings": { - "dynamic": "false", - "_meta": { - "version": 3 - }, - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "properties": { - "ephemeral_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "client": { - "properties": { - "address": { - "type": "keyword", - "ignore_above": 1024 - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "availability_zone": { - "type": "keyword", - "ignore_above": 1024 - }, - "instance": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "machine": { - "properties": { - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "provider": { - "type": "keyword", - "ignore_above": 1024 - }, - "region": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "container": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "image": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "tag": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "runtime": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "destination": { - "properties": { - "address": { - "type": "keyword", - "ignore_above": 1024 - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "dll": { - "properties": { - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024 - }, - "pe": { - "properties": { - "company": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "file_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "original_file_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "type": "keyword", - "ignore_above": 1024 - }, - "data": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "ttl": { - "type": "long" - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "header_flags": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "op_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "question": { - "properties": { - "class": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "subdomain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ecs": { - "properties": { - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "error": { - "properties": { - "code": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "message": { - "type": "text", - "norms": false - }, - "stack_trace": { - "type": "keyword", - "index": false, - "doc_values": false, - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "event": { - "properties": { - "action": { - "type": "keyword", - "ignore_above": 1024 - }, - "category": { - "type": "keyword", - "ignore_above": 1024 - }, - "code": { - "type": "keyword", - "ignore_above": 1024 - }, - "created": { - "type": "date" - }, - "dataset": { - "type": "keyword", - "ignore_above": 1024 - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "ingested": { - "type": "date" - }, - "kind": { - "type": "keyword", - "ignore_above": 1024 - }, - "module": { - "type": "keyword", - "ignore_above": 1024 - }, - "original": { - "type": "keyword", - "index": false, - "doc_values": false, - "ignore_above": 1024 - }, - "outcome": { - "type": "keyword", - "ignore_above": 1024 - }, - "provider": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "url": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "type": "keyword", - "ignore_above": 1024 - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "type": "keyword", - "ignore_above": 1024 - }, - "directory": { - "type": "keyword", - "ignore_above": 1024 - }, - "drive_letter": { - "type": "keyword", - "ignore_above": 1 - }, - "extension": { - "type": "keyword", - "ignore_above": 1024 - }, - "gid": { - "type": "keyword", - "ignore_above": 1024 - }, - "group": { - "type": "keyword", - "ignore_above": 1024 - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "inode": { - "type": "keyword", - "ignore_above": 1024 - }, - "mime_type": { - "type": "keyword", - "ignore_above": 1024 - }, - "mode": { - "type": "keyword", - "ignore_above": 1024 - }, - "mtime": { - "type": "date" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "owner": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "pe": { - "properties": { - "company": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "file_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "original_file_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "size": { - "type": "long" - }, - "target_path": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "uid": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "host": { - "properties": { - "architecture": { - "type": "keyword", - "ignore_above": 1024 - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hostname": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "os": { - "properties": { - "family": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "kernel": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "platform": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "http": { - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "method": { - "type": "keyword", - "ignore_above": 1024 - }, - "referrer": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "interface": { - "properties": { - "alias": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "labels": { - "type": "object" - }, - "log": { - "properties": { - "level": { - "type": "keyword", - "ignore_above": 1024 - }, - "logger": { - "type": "keyword", - "ignore_above": 1024 - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "integer" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "function": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "original": { - "type": "keyword", - "index": false, - "doc_values": false, - "ignore_above": 1024 - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - } - } - }, - "message": { - "type": "text", - "norms": false - }, - "network": { - "properties": { - "application": { - "type": "keyword", - "ignore_above": 1024 - }, - "bytes": { - "type": "long" - }, - "community_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "direction": { - "type": "keyword", - "ignore_above": 1024 - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "type": "keyword", - "ignore_above": 1024 - }, - "inner": { - "properties": { - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "packets": { - "type": "long" - }, - "protocol": { - "type": "keyword", - "ignore_above": 1024 - }, - "transport": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "observer": { - "properties": { - "egress": { - "properties": { - "interface": { - "properties": { - "alias": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "zone": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hostname": { - "type": "keyword", - "ignore_above": 1024 - }, - "ingress": { - "properties": { - "interface": { - "properties": { - "alias": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "zone": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "os": { - "properties": { - "family": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "kernel": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "platform": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - }, - "serial_number": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "vendor": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "organization": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "os": { - "properties": { - "family": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "kernel": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "platform": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "package": { - "properties": { - "architecture": { - "type": "keyword", - "ignore_above": 1024 - }, - "build_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "checksum": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "install_scope": { - "type": "keyword", - "ignore_above": 1024 - }, - "installed": { - "type": "date" - }, - "license": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - }, - "size": { - "type": "long" - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "pe": { - "properties": { - "company": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "file_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "original_file_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "process": { - "properties": { - "args": { - "type": "keyword", - "ignore_above": 1024 - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "entity_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "executable": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "parent": { - "properties": { - "args": { - "type": "keyword", - "ignore_above": 1024 - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "entity_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "executable": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "title": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "pe": { - "properties": { - "company": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "file_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "original_file_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "title": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "type": "keyword", - "ignore_above": 1024 - }, - "strings": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hive": { - "type": "keyword", - "ignore_above": 1024 - }, - "key": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024 - }, - "value": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "related": { - "properties": { - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "ip": { - "type": "ip" - }, - "user": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "rule": { - "properties": { - "author": { - "type": "keyword", - "ignore_above": 1024 - }, - "category": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "license": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - }, - "ruleset": { - "type": "keyword", - "ignore_above": 1024 - }, - "uuid": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "server": { - "properties": { - "address": { - "type": "keyword", - "ignore_above": 1024 - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "service": { - "properties": { - "ephemeral_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "node": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "state": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "signal": { - "properties": { - "ancestors": { - "properties": { - "depth": { - "type": "long" - }, - "id": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "rule": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "depth": { - "type": "integer" - }, - "group": { - "properties": { - "id": { - "type": "keyword" - }, - "index": { - "type": "integer" - } - } - }, - "original_event": { - "properties": { - "action": { - "type": "keyword" - }, - "category": { - "type": "keyword" - }, - "code": { - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "kind": { - "type": "keyword" - }, - "module": { - "type": "keyword" - }, - "original": { - "type": "keyword", - "index": false, - "doc_values": false - }, - "outcome": { - "type": "keyword" - }, - "provider": { - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "original_signal": { - "type": "object", - "dynamic": "false", - "enabled": false - }, - "original_time": { - "type": "date" - }, - "parent": { - "properties": { - "depth": { - "type": "long" - }, - "id": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "rule": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "parents": { - "properties": { - "depth": { - "type": "long" - }, - "id": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "rule": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "author": { - "type": "keyword" - }, - "building_block_type": { - "type": "keyword" - }, - "created_at": { - "type": "date" - }, - "created_by": { - "type": "keyword" - }, - "description": { - "type": "keyword" - }, - "enabled": { - "type": "keyword" - }, - "false_positives": { - "type": "keyword" - }, - "filters": { - "type": "object" - }, - "from": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "immutable": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "interval": { - "type": "keyword" - }, - "language": { - "type": "keyword" - }, - "license": { - "type": "keyword" - }, - "max_signals": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "note": { - "type": "text" - }, - "output_index": { - "type": "keyword" - }, - "query": { - "type": "keyword" - }, - "references": { - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_mapping": { - "properties": { - "field": { - "type": "keyword" - }, - "operator": { - "type": "keyword" - }, - "value": { - "type": "keyword" - } - } - }, - "rule_id": { - "type": "keyword" - }, - "rule_name_override": { - "type": "keyword" - }, - "saved_id": { - "type": "keyword" - }, - "severity": { - "type": "keyword" - }, - "severity_mapping": { - "properties": { - "field": { - "type": "keyword" - }, - "operator": { - "type": "keyword" - }, - "severity": { - "type": "keyword" - }, - "value": { - "type": "keyword" - } - } - }, - "size": { - "type": "keyword" - }, - "tags": { - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "reference": { - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "reference": { - "type": "keyword" - } - } - } - } - }, - "threshold": { - "properties": { - "field": { - "type": "keyword" - }, - "value": { - "type": "float" - } - } - }, - "timeline_id": { - "type": "keyword" - }, - "timeline_title": { - "type": "keyword" - }, - "timestamp_override": { - "type": "keyword" - }, - "to": { - "type": "keyword" - }, - "type": { - "type": "keyword" - }, - "updated_at": { - "type": "date" - }, - "updated_by": { - "type": "keyword" - }, - "version": { - "type": "keyword" - } - } - }, - "status": { - "type": "keyword" - }, - "threshold_count": { - "type": "float" - }, - "threshold_result": { - "properties": { - "count": { - "type": "long" - }, - "value": { - "type": "keyword" - } - } - } - } - }, - "source": { - "properties": { - "address": { - "type": "keyword", - "ignore_above": 1024 - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "tags": { - "type": "keyword", - "ignore_above": 1024 - }, - "threat": { - "properties": { - "framework": { - "type": "keyword", - "ignore_above": 1024 - }, - "tactic": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "technique": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "tls": { - "properties": { - "cipher": { - "type": "keyword", - "ignore_above": 1024 - }, - "client": { - "properties": { - "certificate": { - "type": "keyword", - "ignore_above": 1024 - }, - "certificate_chain": { - "type": "keyword", - "ignore_above": 1024 - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "issuer": { - "type": "keyword", - "ignore_above": 1024 - }, - "ja3": { - "type": "keyword", - "ignore_above": 1024 - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject": { - "type": "keyword", - "ignore_above": 1024 - }, - "supported_ciphers": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "curve": { - "type": "keyword", - "ignore_above": 1024 - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "type": "keyword", - "ignore_above": 1024 - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "type": "keyword", - "ignore_above": 1024 - }, - "certificate_chain": { - "type": "keyword", - "ignore_above": 1024 - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "issuer": { - "type": "keyword", - "ignore_above": 1024 - }, - "ja3s": { - "type": "keyword", - "ignore_above": 1024 - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - }, - "version_protocol": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "trace": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "transaction": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "url": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "extension": { - "type": "keyword", - "ignore_above": 1024 - }, - "fragment": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "original": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "password": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024 - }, - "port": { - "type": "long" - }, - "query": { - "type": "keyword", - "ignore_above": 1024 - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "scheme": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "username": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "user_agent": { - "properties": { - "device": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "original": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "os": { - "properties": { - "family": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "kernel": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "platform": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "vulnerability": { - "properties": { - "category": { - "type": "keyword", - "ignore_above": 1024 - }, - "classification": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "enumeration": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - }, - "report_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "scanner": { - "properties": { - "vendor": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "severity": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "settings": { - "index": { - "lifecycle": { - "name": ".siem-signals-default", - "rollover_alias": ".siem-signals-default" - }, - "number_of_replicas": "1", - "number_of_shards": "1" - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - "auditbeat-7.6.2": { - "is_write_index": true - } - }, - "index": "auditbeat-7.6.2-2020.03.20-000001", - "mappings": { - "_meta": { - "beat": "auditbeat", - "version": "7.6.2" - }, - "date_detection": false, - "dynamic_templates": [ - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "container.labels.*" - } - }, - { - "dns.answers": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "dns.answers.*" - } - }, - { - "log.syslog": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "log.syslog.*" - } - }, - { - "fields": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "fields.*" - } - }, - { - "docker.container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "docker.container.labels.*" - } - }, - { - "kubernetes.labels.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.labels.*" - } - }, - { - "kubernetes.annotations.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.annotations.*" - } - }, - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "auditd": { - "properties": { - "data": { - "properties": { - "a0": { - "ignore_above": 1024, - "type": "keyword" - }, - "a1": { - "ignore_above": 1024, - "type": "keyword" - }, - "a2": { - "ignore_above": 1024, - "type": "keyword" - }, - "a3": { - "ignore_above": 1024, - "type": "keyword" - }, - "a[0-3]": { - "ignore_above": 1024, - "type": "keyword" - }, - "acct": { - "ignore_above": 1024, - "type": "keyword" - }, - "acl": { - "ignore_above": 1024, - "type": "keyword" - }, - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "added": { - "ignore_above": 1024, - "type": "keyword" - }, - "addr": { - "ignore_above": 1024, - "type": "keyword" - }, - "apparmor": { - "ignore_above": 1024, - "type": "keyword" - }, - "arch": { - "ignore_above": 1024, - "type": "keyword" - }, - "argc": { - "ignore_above": 1024, - "type": "keyword" - }, - "audit_backlog_limit": { - "ignore_above": 1024, - "type": "keyword" - }, - "audit_backlog_wait_time": { - "ignore_above": 1024, - "type": "keyword" - }, - "audit_enabled": { - "ignore_above": 1024, - "type": "keyword" - }, - "audit_failure": { - "ignore_above": 1024, - "type": "keyword" - }, - "banners": { - "ignore_above": 1024, - "type": "keyword" - }, - "bool": { - "ignore_above": 1024, - "type": "keyword" - }, - "bus": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_fe": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_fi": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_fp": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_fver": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_pe": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_pi": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_pp": { - "ignore_above": 1024, - "type": "keyword" - }, - "capability": { - "ignore_above": 1024, - "type": "keyword" - }, - "cgroup": { - "ignore_above": 1024, - "type": "keyword" - }, - "changed": { - "ignore_above": 1024, - "type": "keyword" - }, - "cipher": { - "ignore_above": 1024, - "type": "keyword" - }, - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "cmd": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "compat": { - "ignore_above": 1024, - "type": "keyword" - }, - "daddr": { - "ignore_above": 1024, - "type": "keyword" - }, - "data": { - "ignore_above": 1024, - "type": "keyword" - }, - "default-context": { - "ignore_above": 1024, - "type": "keyword" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "dir": { - "ignore_above": 1024, - "type": "keyword" - }, - "direction": { - "ignore_above": 1024, - "type": "keyword" - }, - "dmac": { - "ignore_above": 1024, - "type": "keyword" - }, - "dport": { - "ignore_above": 1024, - "type": "keyword" - }, - "enforcing": { - "ignore_above": 1024, - "type": "keyword" - }, - "entries": { - "ignore_above": 1024, - "type": "keyword" - }, - "exit": { - "ignore_above": 1024, - "type": "keyword" - }, - "fam": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "fd": { - "ignore_above": 1024, - "type": "keyword" - }, - "fe": { - "ignore_above": 1024, - "type": "keyword" - }, - "feature": { - "ignore_above": 1024, - "type": "keyword" - }, - "fi": { - "ignore_above": 1024, - "type": "keyword" - }, - "file": { - "ignore_above": 1024, - "type": "keyword" - }, - "flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "format": { - "ignore_above": 1024, - "type": "keyword" - }, - "fp": { - "ignore_above": 1024, - "type": "keyword" - }, - "fver": { - "ignore_above": 1024, - "type": "keyword" - }, - "grantors": { - "ignore_above": 1024, - "type": "keyword" - }, - "grp": { - "ignore_above": 1024, - "type": "keyword" - }, - "hook": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "icmp_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "igid": { - "ignore_above": 1024, - "type": "keyword" - }, - "img-ctx": { - "ignore_above": 1024, - "type": "keyword" - }, - "inif": { - "ignore_above": 1024, - "type": "keyword" - }, - "ino": { - "ignore_above": 1024, - "type": "keyword" - }, - "inode_gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "inode_uid": { - "ignore_above": 1024, - "type": "keyword" - }, - "invalid_context": { - "ignore_above": 1024, - "type": "keyword" - }, - "ioctlcmd": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "ignore_above": 1024, - "type": "keyword" - }, - "ipid": { - "ignore_above": 1024, - "type": "keyword" - }, - "ipx-net": { - "ignore_above": 1024, - "type": "keyword" - }, - "items": { - "ignore_above": 1024, - "type": "keyword" - }, - "iuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "ksize": { - "ignore_above": 1024, - "type": "keyword" - }, - "laddr": { - "ignore_above": 1024, - "type": "keyword" - }, - "len": { - "ignore_above": 1024, - "type": "keyword" - }, - "list": { - "ignore_above": 1024, - "type": "keyword" - }, - "lport": { - "ignore_above": 1024, - "type": "keyword" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "macproto": { - "ignore_above": 1024, - "type": "keyword" - }, - "maj": { - "ignore_above": 1024, - "type": "keyword" - }, - "major": { - "ignore_above": 1024, - "type": "keyword" - }, - "minor": { - "ignore_above": 1024, - "type": "keyword" - }, - "model": { - "ignore_above": 1024, - "type": "keyword" - }, - "msg": { - "ignore_above": 1024, - "type": "keyword" - }, - "nargs": { - "ignore_above": 1024, - "type": "keyword" - }, - "net": { - "ignore_above": 1024, - "type": "keyword" - }, - "new": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-chardev": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-disk": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-enabled": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-fs": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-level": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-log_passwd": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-mem": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-net": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-range": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-rng": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-role": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-seuser": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-vcpu": { - "ignore_above": 1024, - "type": "keyword" - }, - "new_gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "new_lock": { - "ignore_above": 1024, - "type": "keyword" - }, - "new_pe": { - "ignore_above": 1024, - "type": "keyword" - }, - "new_pi": { - "ignore_above": 1024, - "type": "keyword" - }, - "new_pp": { - "ignore_above": 1024, - "type": "keyword" - }, - "nlnk-fam": { - "ignore_above": 1024, - "type": "keyword" - }, - "nlnk-grp": { - "ignore_above": 1024, - "type": "keyword" - }, - "nlnk-pid": { - "ignore_above": 1024, - "type": "keyword" - }, - "oauid": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_uid": { - "ignore_above": 1024, - "type": "keyword" - }, - "ocomm": { - "ignore_above": 1024, - "type": "keyword" - }, - "oflag": { - "ignore_above": 1024, - "type": "keyword" - }, - "old": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-auid": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-chardev": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-disk": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-enabled": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-fs": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-level": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-log_passwd": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-mem": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-net": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-range": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-rng": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-role": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-ses": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-seuser": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-vcpu": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_enforcing": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_lock": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_pe": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_pi": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_pp": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_prom": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_val": { - "ignore_above": 1024, - "type": "keyword" - }, - "op": { - "ignore_above": 1024, - "type": "keyword" - }, - "opid": { - "ignore_above": 1024, - "type": "keyword" - }, - "oses": { - "ignore_above": 1024, - "type": "keyword" - }, - "outif": { - "ignore_above": 1024, - "type": "keyword" - }, - "parent": { - "ignore_above": 1024, - "type": "keyword" - }, - "per": { - "ignore_above": 1024, - "type": "keyword" - }, - "perm": { - "ignore_above": 1024, - "type": "keyword" - }, - "perm_mask": { - "ignore_above": 1024, - "type": "keyword" - }, - "permissive": { - "ignore_above": 1024, - "type": "keyword" - }, - "pfs": { - "ignore_above": 1024, - "type": "keyword" - }, - "printer": { - "ignore_above": 1024, - "type": "keyword" - }, - "prom": { - "ignore_above": 1024, - "type": "keyword" - }, - "proto": { - "ignore_above": 1024, - "type": "keyword" - }, - "qbytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "range": { - "ignore_above": 1024, - "type": "keyword" - }, - "reason": { - "ignore_above": 1024, - "type": "keyword" - }, - "removed": { - "ignore_above": 1024, - "type": "keyword" - }, - "res": { - "ignore_above": 1024, - "type": "keyword" - }, - "resrc": { - "ignore_above": 1024, - "type": "keyword" - }, - "rport": { - "ignore_above": 1024, - "type": "keyword" - }, - "sauid": { - "ignore_above": 1024, - "type": "keyword" - }, - "scontext": { - "ignore_above": 1024, - "type": "keyword" - }, - "selected-context": { - "ignore_above": 1024, - "type": "keyword" - }, - "seperm": { - "ignore_above": 1024, - "type": "keyword" - }, - "seperms": { - "ignore_above": 1024, - "type": "keyword" - }, - "seqno": { - "ignore_above": 1024, - "type": "keyword" - }, - "seresult": { - "ignore_above": 1024, - "type": "keyword" - }, - "ses": { - "ignore_above": 1024, - "type": "keyword" - }, - "seuser": { - "ignore_above": 1024, - "type": "keyword" - }, - "sig": { - "ignore_above": 1024, - "type": "keyword" - }, - "sigev_signo": { - "ignore_above": 1024, - "type": "keyword" - }, - "smac": { - "ignore_above": 1024, - "type": "keyword" - }, - "socket": { - "properties": { - "addr": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "ignore_above": 1024, - "type": "keyword" - }, - "saddr": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "spid": { - "ignore_above": 1024, - "type": "keyword" - }, - "sport": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "subj": { - "ignore_above": 1024, - "type": "keyword" - }, - "success": { - "ignore_above": 1024, - "type": "keyword" - }, - "syscall": { - "ignore_above": 1024, - "type": "keyword" - }, - "table": { - "ignore_above": 1024, - "type": "keyword" - }, - "tclass": { - "ignore_above": 1024, - "type": "keyword" - }, - "tcontext": { - "ignore_above": 1024, - "type": "keyword" - }, - "terminal": { - "ignore_above": 1024, - "type": "keyword" - }, - "tty": { - "ignore_above": 1024, - "type": "keyword" - }, - "unit": { - "ignore_above": 1024, - "type": "keyword" - }, - "uri": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "val": { - "ignore_above": 1024, - "type": "keyword" - }, - "ver": { - "ignore_above": 1024, - "type": "keyword" - }, - "virt": { - "ignore_above": 1024, - "type": "keyword" - }, - "vm": { - "ignore_above": 1024, - "type": "keyword" - }, - "vm-ctx": { - "ignore_above": 1024, - "type": "keyword" - }, - "vm-pid": { - "ignore_above": 1024, - "type": "keyword" - }, - "watch": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "message_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "paths": { - "properties": { - "dev": { - "ignore_above": 1024, - "type": "keyword" - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "item": { - "ignore_above": 1024, - "type": "keyword" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "nametype": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_level": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_role": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_user": { - "ignore_above": 1024, - "type": "keyword" - }, - "objtype": { - "ignore_above": 1024, - "type": "keyword" - }, - "ogid": { - "ignore_above": 1024, - "type": "keyword" - }, - "ouid": { - "ignore_above": 1024, - "type": "keyword" - }, - "rdev": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "result": { - "ignore_above": 1024, - "type": "keyword" - }, - "sequence": { - "type": "long" - }, - "session": { - "ignore_above": 1024, - "type": "keyword" - }, - "summary": { - "properties": { - "actor": { - "properties": { - "primary": { - "ignore_above": 1024, - "type": "keyword" - }, - "secondary": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "how": { - "ignore_above": 1024, - "type": "keyword" - }, - "object": { - "properties": { - "primary": { - "ignore_above": 1024, - "type": "keyword" - }, - "secondary": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "client": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "availability_zone": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "instance": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "machine": { - "properties": { - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "project": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "region": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "container": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "tag": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "runtime": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "destination": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "data": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ttl": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "header_flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "op_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "question": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "subdomain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "docker": { - "properties": { - "container": { - "properties": { - "labels": { - "type": "object" - } - } - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "norms": false, - "type": "text" - }, - "stack_trace": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "event": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingested": { - "type": "date" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "outcome": { - "ignore_above": 1024, - "type": "keyword" - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "fields": { - "type": "object" - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "directory": { - "ignore_above": 1024, - "type": "keyword" - }, - "drive_letter": { - "ignore_above": 1, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mtime": { - "type": "date" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "fields": { - "raw": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "owner": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "selinux": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "role": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "setgid": { - "type": "boolean" - }, - "setuid": { - "type": "boolean" - }, - "size": { - "type": "long" - }, - "target_path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geoip": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "properties": { - "blake2b_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "blake2b_384": { - "ignore_above": 1024, - "type": "keyword" - }, - "blake2b_512": { - "ignore_above": 1024, - "type": "keyword" - }, - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha384": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_384": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_512": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512_224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "xxh64": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "containerized": { - "type": "boolean" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "build": { - "ignore_above": 1024, - "type": "keyword" - }, - "codename": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "http": { - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "referrer": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "jolokia": { - "properties": { - "agent": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "secured": { - "type": "boolean" - }, - "server": { - "properties": { - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kubernetes": { - "properties": { - "annotations": { - "properties": { - "*": { - "type": "object" - } - } - }, - "container": { - "properties": { - "image": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "deployment": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "properties": { - "*": { - "type": "object" - } - } - }, - "namespace": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pod": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "replicaset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "statefulset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "labels": { - "type": "object" - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "network": { - "properties": { - "application": { - "ignore_above": 1024, - "type": "keyword" - }, - "bytes": { - "type": "long" - }, - "community_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "direction": { - "ignore_above": 1024, - "type": "keyword" - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "packets": { - "type": "long" - }, - "protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "observer": { - "properties": { - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "organization": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "package": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "build_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "checksum": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "install_scope": { - "ignore_above": 1024, - "type": "keyword" - }, - "installed": { - "type": "date" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "process": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "blake2b_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "blake2b_384": { - "ignore_above": 1024, - "type": "keyword" - }, - "blake2b_512": { - "ignore_above": 1024, - "type": "keyword" - }, - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha384": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_384": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_512": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512_224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "xxh64": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "parent": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "strings": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hive": { - "ignore_above": 1024, - "type": "keyword" - }, - "key": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "related": { - "properties": { - "ip": { - "type": "ip" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "ruleset": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "server": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "service": { - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "socket": { - "properties": { - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "system": { - "properties": { - "audit": { - "properties": { - "host": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "boottime": { - "type": "date" - }, - "containerized": { - "type": "boolean" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "codename": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timezone": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "offset": { - "properties": { - "sec": { - "type": "long" - } - } - } - } - }, - "uptime": { - "type": "long" - } - } - }, - "package": { - "properties": { - "arch": { - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "installtime": { - "type": "date" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "release": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "summary": { - "ignore_above": 1024, - "type": "keyword" - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user": { - "properties": { - "dir": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "type": "object" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "password": { - "properties": { - "last_changed": { - "type": "date" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "shell": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - }, - "user_information": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "tags": { - "ignore_above": 1024, - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "ignore_above": 1024, - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "timeseries": { - "properties": { - "instance": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tls": { - "properties": { - "cipher": { - "ignore_above": 1024, - "type": "keyword" - }, - "client": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - }, - "supported_ciphers": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "curve": { - "ignore_above": 1024, - "type": "keyword" - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3s": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_protocol": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tracing": { - "properties": { - "trace": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "transaction": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "url": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "fragment": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "password": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "query": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "scheme": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "username": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user": { - "properties": { - "audit": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "effective": { - "properties": { - "group": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "filesystem": { - "properties": { - "group": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "name_map": { - "type": "object" - }, - "saved": { - "properties": { - "group": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "selinux": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "role": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "terminal": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user_agent": { - "properties": { - "device": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vulnerability": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "classification": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "enumeration": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "report_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "scanner": { - "properties": { - "vendor": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "severity": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "settings": { - "index": { - "lifecycle": { - "name": "auditbeat", - "rollover_alias": "auditbeat-7.6.2" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "number_of_replicas": "1", - "number_of_shards": "1", - "query": { - "default_field": [ - "message", - "tags", - "agent.ephemeral_id", - "agent.id", - "agent.name", - "agent.type", - "agent.version", - "as.organization.name", - "client.address", - "client.as.organization.name", - "client.domain", - "client.geo.city_name", - "client.geo.continent_name", - "client.geo.country_iso_code", - "client.geo.country_name", - "client.geo.name", - "client.geo.region_iso_code", - "client.geo.region_name", - "client.mac", - "client.registered_domain", - "client.top_level_domain", - "client.user.domain", - "client.user.email", - "client.user.full_name", - "client.user.group.domain", - "client.user.group.id", - "client.user.group.name", - "client.user.hash", - "client.user.id", - "client.user.name", - "cloud.account.id", - "cloud.availability_zone", - "cloud.instance.id", - "cloud.instance.name", - "cloud.machine.type", - "cloud.provider", - "cloud.region", - "container.id", - "container.image.name", - "container.image.tag", - "container.name", - "container.runtime", - "destination.address", - "destination.as.organization.name", - "destination.domain", - "destination.geo.city_name", - "destination.geo.continent_name", - "destination.geo.country_iso_code", - "destination.geo.country_name", - "destination.geo.name", - "destination.geo.region_iso_code", - "destination.geo.region_name", - "destination.mac", - "destination.registered_domain", - "destination.top_level_domain", - "destination.user.domain", - "destination.user.email", - "destination.user.full_name", - "destination.user.group.domain", - "destination.user.group.id", - "destination.user.group.name", - "destination.user.hash", - "destination.user.id", - "destination.user.name", - "dns.answers.class", - "dns.answers.data", - "dns.answers.name", - "dns.answers.type", - "dns.header_flags", - "dns.id", - "dns.op_code", - "dns.question.class", - "dns.question.name", - "dns.question.registered_domain", - "dns.question.subdomain", - "dns.question.top_level_domain", - "dns.question.type", - "dns.response_code", - "dns.type", - "ecs.version", - "error.code", - "error.id", - "error.message", - "error.stack_trace", - "error.type", - "event.action", - "event.category", - "event.code", - "event.dataset", - "event.hash", - "event.id", - "event.kind", - "event.module", - "event.original", - "event.outcome", - "event.provider", - "event.timezone", - "event.type", - "file.device", - "file.directory", - "file.extension", - "file.gid", - "file.group", - "file.hash.md5", - "file.hash.sha1", - "file.hash.sha256", - "file.hash.sha512", - "file.inode", - "file.mode", - "file.name", - "file.owner", - "file.path", - "file.target_path", - "file.type", - "file.uid", - "geo.city_name", - "geo.continent_name", - "geo.country_iso_code", - "geo.country_name", - "geo.name", - "geo.region_iso_code", - "geo.region_name", - "group.domain", - "group.id", - "group.name", - "hash.md5", - "hash.sha1", - "hash.sha256", - "hash.sha512", - "host.architecture", - "host.geo.city_name", - "host.geo.continent_name", - "host.geo.country_iso_code", - "host.geo.country_name", - "host.geo.name", - "host.geo.region_iso_code", - "host.geo.region_name", - "host.hostname", - "host.id", - "host.mac", - "host.name", - "host.os.family", - "host.os.full", - "host.os.kernel", - "host.os.name", - "host.os.platform", - "host.os.version", - "host.type", - "host.user.domain", - "host.user.email", - "host.user.full_name", - "host.user.group.domain", - "host.user.group.id", - "host.user.group.name", - "host.user.hash", - "host.user.id", - "host.user.name", - "http.request.body.content", - "http.request.method", - "http.request.referrer", - "http.response.body.content", - "http.version", - "log.level", - "log.logger", - "log.origin.file.name", - "log.origin.function", - "log.original", - "log.syslog.facility.name", - "log.syslog.severity.name", - "network.application", - "network.community_id", - "network.direction", - "network.iana_number", - "network.name", - "network.protocol", - "network.transport", - "network.type", - "observer.geo.city_name", - "observer.geo.continent_name", - "observer.geo.country_iso_code", - "observer.geo.country_name", - "observer.geo.name", - "observer.geo.region_iso_code", - "observer.geo.region_name", - "observer.hostname", - "observer.mac", - "observer.name", - "observer.os.family", - "observer.os.full", - "observer.os.kernel", - "observer.os.name", - "observer.os.platform", - "observer.os.version", - "observer.product", - "observer.serial_number", - "observer.type", - "observer.vendor", - "observer.version", - "organization.id", - "organization.name", - "os.family", - "os.full", - "os.kernel", - "os.name", - "os.platform", - "os.version", - "package.architecture", - "package.checksum", - "package.description", - "package.install_scope", - "package.license", - "package.name", - "package.path", - "package.version", - "process.args", - "text", - "process.executable", - "process.hash.md5", - "process.hash.sha1", - "process.hash.sha256", - "process.hash.sha512", - "process.name", - "text", - "text", - "text", - "text", - "text", - "process.thread.name", - "process.title", - "process.working_directory", - "server.address", - "server.as.organization.name", - "server.domain", - "server.geo.city_name", - "server.geo.continent_name", - "server.geo.country_iso_code", - "server.geo.country_name", - "server.geo.name", - "server.geo.region_iso_code", - "server.geo.region_name", - "server.mac", - "server.registered_domain", - "server.top_level_domain", - "server.user.domain", - "server.user.email", - "server.user.full_name", - "server.user.group.domain", - "server.user.group.id", - "server.user.group.name", - "server.user.hash", - "server.user.id", - "server.user.name", - "service.ephemeral_id", - "service.id", - "service.name", - "service.node.name", - "service.state", - "service.type", - "service.version", - "source.address", - "source.as.organization.name", - "source.domain", - "source.geo.city_name", - "source.geo.continent_name", - "source.geo.country_iso_code", - "source.geo.country_name", - "source.geo.name", - "source.geo.region_iso_code", - "source.geo.region_name", - "source.mac", - "source.registered_domain", - "source.top_level_domain", - "source.user.domain", - "source.user.email", - "source.user.full_name", - "source.user.group.domain", - "source.user.group.id", - "source.user.group.name", - "source.user.hash", - "source.user.id", - "source.user.name", - "threat.framework", - "threat.tactic.id", - "threat.tactic.name", - "threat.tactic.reference", - "threat.technique.id", - "threat.technique.name", - "threat.technique.reference", - "tracing.trace.id", - "tracing.transaction.id", - "url.domain", - "url.extension", - "url.fragment", - "url.full", - "url.original", - "url.password", - "url.path", - "url.query", - "url.registered_domain", - "url.scheme", - "url.top_level_domain", - "url.username", - "user.domain", - "user.email", - "user.full_name", - "user.group.domain", - "user.group.id", - "user.group.name", - "user.hash", - "user.id", - "user.name", - "user_agent.device.name", - "user_agent.name", - "text", - "user_agent.original", - "user_agent.os.family", - "user_agent.os.full", - "user_agent.os.kernel", - "user_agent.os.name", - "user_agent.os.platform", - "user_agent.os.version", - "user_agent.version", - "text", - "agent.hostname", - "timeseries.instance", - "cloud.project.id", - "cloud.image.id", - "host.os.build", - "host.os.codename", - "kubernetes.pod.name", - "kubernetes.pod.uid", - "kubernetes.namespace", - "kubernetes.node.name", - "kubernetes.replicaset.name", - "kubernetes.deployment.name", - "kubernetes.statefulset.name", - "kubernetes.container.name", - "kubernetes.container.image", - "jolokia.agent.version", - "jolokia.agent.id", - "jolokia.server.product", - "jolokia.server.version", - "jolokia.server.vendor", - "jolokia.url", - "raw", - "file.origin", - "file.selinux.user", - "file.selinux.role", - "file.selinux.domain", - "file.selinux.level", - "user.audit.id", - "user.audit.name", - "user.effective.id", - "user.effective.name", - "user.effective.group.id", - "user.effective.group.name", - "user.filesystem.id", - "user.filesystem.name", - "user.filesystem.group.id", - "user.filesystem.group.name", - "user.saved.id", - "user.saved.name", - "user.saved.group.id", - "user.saved.group.name", - "user.selinux.user", - "user.selinux.role", - "user.selinux.domain", - "user.selinux.level", - "user.selinux.category", - "source.path", - "destination.path", - "auditd.message_type", - "auditd.session", - "auditd.result", - "auditd.summary.actor.primary", - "auditd.summary.actor.secondary", - "auditd.summary.object.type", - "auditd.summary.object.primary", - "auditd.summary.object.secondary", - "auditd.summary.how", - "auditd.paths.inode", - "auditd.paths.dev", - "auditd.paths.obj_user", - "auditd.paths.obj_role", - "auditd.paths.obj_domain", - "auditd.paths.obj_level", - "auditd.paths.objtype", - "auditd.paths.ouid", - "auditd.paths.rdev", - "auditd.paths.nametype", - "auditd.paths.ogid", - "auditd.paths.item", - "auditd.paths.mode", - "auditd.paths.name", - "auditd.data.action", - "auditd.data.minor", - "auditd.data.acct", - "auditd.data.addr", - "auditd.data.cipher", - "auditd.data.id", - "auditd.data.entries", - "auditd.data.kind", - "auditd.data.ksize", - "auditd.data.spid", - "auditd.data.arch", - "auditd.data.argc", - "auditd.data.major", - "auditd.data.unit", - "auditd.data.table", - "auditd.data.terminal", - "auditd.data.grantors", - "auditd.data.direction", - "auditd.data.op", - "auditd.data.tty", - "auditd.data.syscall", - "auditd.data.data", - "auditd.data.family", - "auditd.data.mac", - "auditd.data.pfs", - "auditd.data.items", - "auditd.data.a0", - "auditd.data.a1", - "auditd.data.a2", - "auditd.data.a3", - "auditd.data.hostname", - "auditd.data.lport", - "auditd.data.rport", - "auditd.data.exit", - "auditd.data.fp", - "auditd.data.laddr", - "auditd.data.sport", - "auditd.data.capability", - "auditd.data.nargs", - "auditd.data.new-enabled", - "auditd.data.audit_backlog_limit", - "auditd.data.dir", - "auditd.data.cap_pe", - "auditd.data.model", - "auditd.data.new_pp", - "auditd.data.old-enabled", - "auditd.data.oauid", - "auditd.data.old", - "auditd.data.banners", - "auditd.data.feature", - "auditd.data.vm-ctx", - "auditd.data.opid", - "auditd.data.seperms", - "auditd.data.seresult", - "auditd.data.new-rng", - "auditd.data.old-net", - "auditd.data.sigev_signo", - "auditd.data.ino", - "auditd.data.old_enforcing", - "auditd.data.old-vcpu", - "auditd.data.range", - "auditd.data.res", - "auditd.data.added", - "auditd.data.fam", - "auditd.data.nlnk-pid", - "auditd.data.subj", - "auditd.data.a[0-3]", - "auditd.data.cgroup", - "auditd.data.kernel", - "auditd.data.ocomm", - "auditd.data.new-net", - "auditd.data.permissive", - "auditd.data.class", - "auditd.data.compat", - "auditd.data.fi", - "auditd.data.changed", - "auditd.data.msg", - "auditd.data.dport", - "auditd.data.new-seuser", - "auditd.data.invalid_context", - "auditd.data.dmac", - "auditd.data.ipx-net", - "auditd.data.iuid", - "auditd.data.macproto", - "auditd.data.obj", - "auditd.data.ipid", - "auditd.data.new-fs", - "auditd.data.vm-pid", - "auditd.data.cap_pi", - "auditd.data.old-auid", - "auditd.data.oses", - "auditd.data.fd", - "auditd.data.igid", - "auditd.data.new-disk", - "auditd.data.parent", - "auditd.data.len", - "auditd.data.oflag", - "auditd.data.uuid", - "auditd.data.code", - "auditd.data.nlnk-grp", - "auditd.data.cap_fp", - "auditd.data.new-mem", - "auditd.data.seperm", - "auditd.data.enforcing", - "auditd.data.new-chardev", - "auditd.data.old-rng", - "auditd.data.outif", - "auditd.data.cmd", - "auditd.data.hook", - "auditd.data.new-level", - "auditd.data.sauid", - "auditd.data.sig", - "auditd.data.audit_backlog_wait_time", - "auditd.data.printer", - "auditd.data.old-mem", - "auditd.data.perm", - "auditd.data.old_pi", - "auditd.data.state", - "auditd.data.format", - "auditd.data.new_gid", - "auditd.data.tcontext", - "auditd.data.maj", - "auditd.data.watch", - "auditd.data.device", - "auditd.data.grp", - "auditd.data.bool", - "auditd.data.icmp_type", - "auditd.data.new_lock", - "auditd.data.old_prom", - "auditd.data.acl", - "auditd.data.ip", - "auditd.data.new_pi", - "auditd.data.default-context", - "auditd.data.inode_gid", - "auditd.data.new-log_passwd", - "auditd.data.new_pe", - "auditd.data.selected-context", - "auditd.data.cap_fver", - "auditd.data.file", - "auditd.data.net", - "auditd.data.virt", - "auditd.data.cap_pp", - "auditd.data.old-range", - "auditd.data.resrc", - "auditd.data.new-range", - "auditd.data.obj_gid", - "auditd.data.proto", - "auditd.data.old-disk", - "auditd.data.audit_failure", - "auditd.data.inif", - "auditd.data.vm", - "auditd.data.flags", - "auditd.data.nlnk-fam", - "auditd.data.old-fs", - "auditd.data.old-ses", - "auditd.data.seqno", - "auditd.data.fver", - "auditd.data.qbytes", - "auditd.data.seuser", - "auditd.data.cap_fe", - "auditd.data.new-vcpu", - "auditd.data.old-level", - "auditd.data.old_pp", - "auditd.data.daddr", - "auditd.data.old-role", - "auditd.data.ioctlcmd", - "auditd.data.smac", - "auditd.data.apparmor", - "auditd.data.fe", - "auditd.data.perm_mask", - "auditd.data.ses", - "auditd.data.cap_fi", - "auditd.data.obj_uid", - "auditd.data.reason", - "auditd.data.list", - "auditd.data.old_lock", - "auditd.data.bus", - "auditd.data.old_pe", - "auditd.data.new-role", - "auditd.data.prom", - "auditd.data.uri", - "auditd.data.audit_enabled", - "auditd.data.old-log_passwd", - "auditd.data.old-seuser", - "auditd.data.per", - "auditd.data.scontext", - "auditd.data.tclass", - "auditd.data.ver", - "auditd.data.new", - "auditd.data.val", - "auditd.data.img-ctx", - "auditd.data.old-chardev", - "auditd.data.old_val", - "auditd.data.success", - "auditd.data.inode_uid", - "auditd.data.removed", - "auditd.data.socket.port", - "auditd.data.socket.saddr", - "auditd.data.socket.addr", - "auditd.data.socket.family", - "auditd.data.socket.path", - "geoip.continent_name", - "geoip.city_name", - "geoip.region_name", - "geoip.country_iso_code", - "hash.blake2b_256", - "hash.blake2b_384", - "hash.blake2b_512", - "hash.md5", - "hash.sha1", - "hash.sha224", - "hash.sha256", - "hash.sha384", - "hash.sha3_224", - "hash.sha3_256", - "hash.sha3_384", - "hash.sha3_512", - "hash.sha512", - "hash.sha512_224", - "hash.sha512_256", - "hash.xxh64", - "event.origin", - "user.entity_id", - "user.terminal", - "process.entity_id", - "process.hash.blake2b_256", - "process.hash.blake2b_384", - "process.hash.blake2b_512", - "process.hash.sha224", - "process.hash.sha384", - "process.hash.sha3_224", - "process.hash.sha3_256", - "process.hash.sha3_384", - "process.hash.sha3_512", - "process.hash.sha512_224", - "process.hash.sha512_256", - "process.hash.xxh64", - "socket.entity_id", - "system.audit.host.timezone.name", - "system.audit.host.hostname", - "system.audit.host.id", - "system.audit.host.architecture", - "system.audit.host.mac", - "system.audit.host.os.codename", - "system.audit.host.os.platform", - "system.audit.host.os.name", - "system.audit.host.os.family", - "system.audit.host.os.version", - "system.audit.host.os.kernel", - "system.audit.package.entity_id", - "system.audit.package.name", - "system.audit.package.version", - "system.audit.package.release", - "system.audit.package.arch", - "system.audit.package.license", - "system.audit.package.summary", - "system.audit.package.url", - "system.audit.user.name", - "system.audit.user.uid", - "system.audit.user.gid", - "system.audit.user.dir", - "system.audit.user.shell", - "system.audit.user.user_information", - "system.audit.user.password.type", - "fields.*" - ] - }, - "refresh_interval": "5s" - } - } - } -} diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_list.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_list.ts deleted file mode 100644 index 741040b12fd7b..0000000000000 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_list.ts +++ /dev/null @@ -1,158 +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; - * you may not use this file except in compliance with the Elastic License. - */ -import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; -import { PolicyTestResourceInfo } from '../../services/endpoint_policy'; - -export default function ({ getPageObjects, getService }: FtrProviderContext) { - const pageObjects = getPageObjects([ - 'common', - 'endpoint', - 'policy', - 'endpointPageUtils', - 'ingestManagerCreatePackagePolicy', - ]); - const testSubjects = getService('testSubjects'); - const policyTestResources = getService('policyTestResources'); - const RELATIVE_DATE_FORMAT = /\d (?:seconds|minutes) ago/i; - - describe('When on the Endpoint Policy List', function () { - this.tags(['ciGroup7']); - before(async () => { - await pageObjects.policy.navigateToPolicyList(); - }); - - it('loads the Policy List Page', async () => { - await testSubjects.existOrFail('policyListPage'); - }); - it('displays page title', async () => { - const policyTitle = await testSubjects.getVisibleText('header-page-title'); - expect(policyTitle).to.equal('Policies'); - }); - it('shows header create policy button', async () => { - const createButtonTitle = await testSubjects.getVisibleText('headerCreateNewPolicyButton'); - expect(createButtonTitle).to.equal('Create new policy'); - }); - it('shows empty state', async () => { - await testSubjects.existOrFail('emptyPolicyTable'); - }); - - describe('and policies exists', () => { - let policyInfo: PolicyTestResourceInfo; - - before(async () => { - // load/create a policy and then navigate back to the policy view so that the list is refreshed - policyInfo = await policyTestResources.createPolicy(); - await pageObjects.policy.navigateToPolicyList(); - await pageObjects.endpoint.waitForTableToHaveData('policyTable'); - }); - after(async () => { - if (policyInfo) { - await policyInfo.cleanup(); - } - }); - - it('has correct table headers', async () => { - const allHeaderCells = await pageObjects.endpointPageUtils.tableHeaderVisibleText( - 'policyTable' - ); - expect(allHeaderCells).to.eql([ - 'Policy Name', - 'Created By', - 'Created Date', - 'Last Updated By', - 'Last Updated', - 'Version', - 'Actions', - ]); - }); - - it('should show policy on the list', async () => { - const [, policyRow] = await pageObjects.endpointPageUtils.tableData('policyTable'); - // Validate row data with the exception of the Date columns - since those are initially - // shown as relative. - expect([policyRow[0], policyRow[1], policyRow[3], policyRow[5], policyRow[6]]).to.eql([ - 'Protect East Coastrev. 1', - 'elastic', - 'elastic', - `v${policyInfo.packagePolicy.package?.version}`, - '', - ]); - [policyRow[2], policyRow[4]].forEach((relativeDate) => { - expect(relativeDate).to.match(RELATIVE_DATE_FORMAT); - }); - }); - - it('should show agent policy action as a link', async () => { - await (await pageObjects.policy.findFirstActionsButton()).click(); - const agentPolicyLink = await testSubjects.find('agentPolicyLink'); - expect(await agentPolicyLink.getAttribute('href')).to.match( - new RegExp(`\/ingestManager#\/policies\/${policyInfo.agentPolicy.id}$`) - ); - // Close action menu - await (await pageObjects.policy.findFirstActionsButton()).click(); - }); - - it('should delete a policy', async () => { - await pageObjects.policy.launchAndFindDeleteModal(); - await testSubjects.existOrFail('policyListDeleteModal'); - await pageObjects.common.clickConfirmOnModal(); - const emptyPolicyTable = await testSubjects.find('emptyPolicyTable'); - expect(emptyPolicyTable).not.to.be(null); - }); - }); - - describe('and user clicks on page header create button', () => { - beforeEach(async () => { - await pageObjects.policy.navigateToPolicyList(); - await (await pageObjects.policy.findHeaderCreateNewButton()).click(); - }); - - it('should redirect to ingest management integrations add package policy', async () => { - await pageObjects.ingestManagerCreatePackagePolicy.ensureOnCreatePageOrFail(); - }); - - it('should redirect user back to Policy List if Cancel button is clicked', async () => { - await (await pageObjects.ingestManagerCreatePackagePolicy.findCancelButton()).click(); - await pageObjects.policy.ensureIsOnPolicyPage(); - }); - - it('should redirect user back to Policy List if Back link is clicked', async () => { - await (await pageObjects.ingestManagerCreatePackagePolicy.findBackLink()).click(); - await pageObjects.policy.ensureIsOnPolicyPage(); - }); - - it('should display custom endpoint configuration message', async () => { - await pageObjects.ingestManagerCreatePackagePolicy.selectAgentPolicy(); - const endpointConfig = await pageObjects.policy.findPackagePolicyEndpointCustomConfiguration(); - expect(endpointConfig).not.to.be(undefined); - }); - - it('should have empty value for package policy name', async () => { - await pageObjects.ingestManagerCreatePackagePolicy.selectAgentPolicy(); - expect(await pageObjects.ingestManagerCreatePackagePolicy.getPackagePolicyName()).to.be(''); - }); - - it('should redirect user back to Policy List after a successful save', async () => { - const newPolicyName = `endpoint policy ${Date.now()}`; - await pageObjects.ingestManagerCreatePackagePolicy.selectAgentPolicy(); - await pageObjects.ingestManagerCreatePackagePolicy.setPackagePolicyName(newPolicyName); - await (await pageObjects.ingestManagerCreatePackagePolicy.findSaveButton()).click(); - await pageObjects.ingestManagerCreatePackagePolicy.waitForSaveSuccessNotification(); - await pageObjects.policy.ensureIsOnPolicyPage(); - await policyTestResources.deletePolicyByName(newPolicyName); - }); - }); - - describe('and user clicks on page header create button', () => { - it('should direct users to the ingest management integrations add package policy', async () => { - await pageObjects.policy.navigateToPolicyList(); - await (await pageObjects.policy.findOnboardingStartButton()).click(); - await pageObjects.ingestManagerCreatePackagePolicy.ensureOnCreatePageOrFail(); - }); - }); - }); -} diff --git a/x-pack/test/security_solution_endpoint/page_objects/policy_page.ts b/x-pack/test/security_solution_endpoint/page_objects/policy_page.ts index 8bfbdc32452ee..18ffc6229e74f 100644 --- a/x-pack/test/security_solution_endpoint/page_objects/policy_page.ts +++ b/x-pack/test/security_solution_endpoint/page_objects/policy_page.ts @@ -12,42 +12,6 @@ export function EndpointPolicyPageProvider({ getService, getPageObjects }: FtrPr const browser = getService('browser'); return { - /** - * Navigates to the Endpoint Policy List - */ - async navigateToPolicyList() { - await pageObjects.common.navigateToUrlWithBrowserHistory( - 'securitySolutionManagement', - '/policy' - ); - await pageObjects.header.waitUntilLoadingHasFinished(); - }, - - /** - * Finds and returns the Policy Details Page Save button - */ - async findFirstActionsButton() { - await this.ensureIsOnPolicyPage(); - return await testSubjects.find('policyActionsButton'); - }, - - /** - * Finds and returns the Policy Details Page Save button - */ - async launchAndFindDeleteModal() { - const actionsButton = await this.findFirstActionsButton(); - await actionsButton.click(); - await testSubjects.click('policyDeleteButton'); - return await testSubjects.find('policyListDeleteModal'); - }, - - /** - * ensures that the Policy Page is the currently display view - */ - async ensureIsOnPolicyPage() { - await testSubjects.existOrFail('policyListPage'); - }, - /** * Navigates to the Endpoint Policy Details page * @@ -130,13 +94,5 @@ export function EndpointPolicyPageProvider({ getService, getPageObjects }: FtrPr async findPackagePolicyEndpointCustomConfiguration(onEditPage: boolean = false) { return await testSubjects.find(`endpointPackagePolicy_${onEditPage ? 'edit' : 'create'}`); }, - - /** - * Finds and returns the onboarding button displayed in empty List pages - */ - async findOnboardingStartButton() { - await testSubjects.waitForEnabled('onboardingStartButton'); - return await testSubjects.find('onboardingStartButton'); - }, }; } diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/alerts.ts b/x-pack/test/security_solution_endpoint_api_int/apis/resolver/alerts.ts deleted file mode 100644 index bf7ed711b75a5..0000000000000 --- a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/alerts.ts +++ /dev/null @@ -1,164 +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; - * you may not use this file except in compliance with the Elastic License. - */ -import expect from '@kbn/expect'; -import { - eventIDSafeVersion, - timestampSafeVersion, -} from '../../../../plugins/security_solution/common/endpoint/models/event'; -import { ResolverRelatedAlerts } from '../../../../plugins/security_solution/common/endpoint/types'; -import { FtrProviderContext } from '../../ftr_provider_context'; -import { - Tree, - RelatedEventCategory, -} from '../../../../plugins/security_solution/common/endpoint/generate_data'; -import { Options, GeneratedTrees } from '../../services/resolver'; -import { compareArrays } from './common'; - -export default function ({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const resolver = getService('resolverGenerator'); - - const relatedEventsToGen = [ - { category: RelatedEventCategory.Driver, count: 2 }, - { category: RelatedEventCategory.File, count: 1 }, - { category: RelatedEventCategory.Registry, count: 1 }, - ]; - const relatedAlerts = 4; - let resolverTrees: GeneratedTrees; - let tree: Tree; - const treeOptions: Options = { - ancestors: 5, - relatedEvents: relatedEventsToGen, - relatedAlerts, - children: 3, - generations: 2, - percentTerminated: 100, - percentWithRelated: 100, - numTrees: 1, - alwaysGenMaxChildrenPerNode: true, - ancestryArraySize: 2, - }; - - describe('related alerts route', () => { - before(async () => { - resolverTrees = await resolver.createTrees(treeOptions); - // we only requested a single alert so there's only 1 tree - tree = resolverTrees.trees[0]; - }); - after(async () => { - await resolver.deleteData(resolverTrees); - }); - - it('should not find any alerts', async () => { - const { body }: { body: ResolverRelatedAlerts } = await supertest - .post(`/api/endpoint/resolver/5555/alerts`) - .set('kbn-xsrf', 'xxx') - .expect(200); - expect(body.nextAlert).to.eql(null); - expect(body.alerts).to.be.empty(); - }); - - it('should return details for the root node', async () => { - const { body }: { body: ResolverRelatedAlerts } = await supertest - .post(`/api/endpoint/resolver/${tree.origin.id}/alerts`) - .set('kbn-xsrf', 'xxx') - .expect(200); - expect(body.alerts.length).to.eql(4); - compareArrays(tree.origin.relatedAlerts, body.alerts, true); - expect(body.nextAlert).to.eql(null); - }); - - it('should allow alerts to be filtered', async () => { - const filter = `not event.id:"${tree.origin.relatedAlerts[0].event?.id}"`; - const { body }: { body: ResolverRelatedAlerts } = await supertest - .post(`/api/endpoint/resolver/${tree.origin.id}/alerts`) - .set('kbn-xsrf', 'xxx') - .send({ - filter, - }) - .expect(200); - expect(body.alerts.length).to.eql(3); - compareArrays(tree.origin.relatedAlerts, body.alerts); - expect(body.nextAlert).to.eql(null); - - // should not find the alert that we excluded in the filter - expect( - body.alerts.find((bodyAlert) => { - return eventIDSafeVersion(bodyAlert) === tree.origin.relatedAlerts[0].event?.id; - }) - ).to.not.be.ok(); - }); - - it('should return paginated results for the root node', async () => { - let { body }: { body: ResolverRelatedAlerts } = await supertest - .post(`/api/endpoint/resolver/${tree.origin.id}/alerts?alerts=2`) - .set('kbn-xsrf', 'xxx') - .expect(200); - expect(body.alerts.length).to.eql(2); - compareArrays(tree.origin.relatedAlerts, body.alerts); - expect(body.nextAlert).not.to.eql(null); - - ({ body } = await supertest - .post( - `/api/endpoint/resolver/${tree.origin.id}/alerts?alerts=2&afterAlert=${body.nextAlert}` - ) - .set('kbn-xsrf', 'xxx') - .expect(200)); - expect(body.alerts.length).to.eql(2); - compareArrays(tree.origin.relatedAlerts, body.alerts); - expect(body.nextAlert).to.not.eql(null); - - ({ body } = await supertest - .post( - `/api/endpoint/resolver/${tree.origin.id}/alerts?alerts=2&afterAlert=${body.nextAlert}` - ) - .set('kbn-xsrf', 'xxx') - .expect(200)); - expect(body.alerts).to.be.empty(); - expect(body.nextAlert).to.eql(null); - }); - - it('should return the first page of information when the cursor is invalid', async () => { - const { body }: { body: ResolverRelatedAlerts } = await supertest - .post(`/api/endpoint/resolver/${tree.origin.id}/alerts?afterAlert=blah`) - .set('kbn-xsrf', 'xxx') - .expect(200); - expect(body.alerts.length).to.eql(4); - compareArrays(tree.origin.relatedAlerts, body.alerts, true); - expect(body.nextAlert).to.eql(null); - }); - - it('should sort the alerts in ascending order', async () => { - const { body }: { body: ResolverRelatedAlerts } = await supertest - .post(`/api/endpoint/resolver/${tree.origin.id}/alerts`) - .set('kbn-xsrf', 'xxx') - .expect(200); - const sortedAsc = [...tree.origin.relatedAlerts].sort((event1, event2) => { - // this sorts the events by timestamp in ascending order - const diff = (timestampSafeVersion(event1) ?? 0) - (timestampSafeVersion(event2) ?? 0); - const event1ID = eventIDSafeVersion(event1) ?? 0; - const event2ID = eventIDSafeVersion(event2) ?? 0; - // if the timestamps are the same, fallback to the event.id sorted in - // ascending order - if (diff === 0) { - if (event1ID < event2ID) { - return -1; - } - if (event1ID > event2ID) { - return 1; - } - return 0; - } - return diff; - }); - - expect(body.alerts.length).to.eql(4); - for (let i = 0; i < body.alerts.length; i++) { - expect(eventIDSafeVersion(body.alerts[i])).to.equal(sortedAsc[i].event?.id); - } - }); - }); -} diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/children.ts b/x-pack/test/security_solution_endpoint_api_int/apis/resolver/children.ts deleted file mode 100644 index c51b322478f34..0000000000000 --- a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/children.ts +++ /dev/null @@ -1,240 +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; - * you may not use this file except in compliance with the Elastic License. - */ -import expect from '@kbn/expect'; -import { SearchResponse } from 'elasticsearch'; -import { - entityIDSafeVersion, - timestampSafeVersion, -} from '../../../../plugins/security_solution/common/endpoint/models/event'; -import { eventsIndexPattern } from '../../../../plugins/security_solution/common/endpoint/constants'; -import { ChildrenPaginationBuilder } from '../../../../plugins/security_solution/server/endpoint/routes/resolver/utils/children_pagination'; -import { ChildrenQuery } from '../../../../plugins/security_solution/server/endpoint/routes/resolver/queries/children'; -import { - SafeResolverTree, - SafeResolverEvent, - SafeResolverChildren, -} from '../../../../plugins/security_solution/common/endpoint/types'; -import { FtrProviderContext } from '../../ftr_provider_context'; -import { - Event, - EndpointDocGenerator, -} from '../../../../plugins/security_solution/common/endpoint/generate_data'; -import { InsertedEvents, processEventsIndex } from '../../services/resolver'; -import { createAncestryArray } from './common'; - -export default function resolverAPIIntegrationTests({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const resolver = getService('resolverGenerator'); - const generator = new EndpointDocGenerator('resolver'); - const es = getService('es'); - - describe('Resolver children edge cases', () => { - describe('info and exec children', () => { - let origin: Event; - let infoEvent: Event; - let startEvent: Event; - let execEvent: Event; - let genData: InsertedEvents; - - before(async () => { - // Construct the following tree: - // Origin -> infoEvent -> startEvent -> execEvent - origin = generator.generateEvent({ - eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), - }); - infoEvent = generator.generateEvent({ - parentEntityID: entityIDSafeVersion(origin), - ancestry: createAncestryArray([origin]), - eventType: ['info'], - eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), - }); - - startEvent = generator.generateEvent({ - timestamp: (timestampSafeVersion(infoEvent) ?? 0) + 100, - parentEntityID: entityIDSafeVersion(infoEvent), - ancestry: createAncestryArray([infoEvent, origin]), - eventType: ['start'], - eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), - }); - - execEvent = generator.generateEvent({ - timestamp: (timestampSafeVersion(startEvent) ?? 0) + 100, - parentEntityID: entityIDSafeVersion(startEvent), - ancestry: createAncestryArray([startEvent, infoEvent]), - eventType: ['change'], - eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), - }); - genData = await resolver.insertEvents( - [origin, infoEvent, startEvent, execEvent], - processEventsIndex - ); - }); - - after(async () => { - await resolver.deleteData(genData); - }); - - it('finds all the children of the origin', async () => { - const { body }: { body: SafeResolverTree } = await supertest - .get(`/api/endpoint/resolver/${origin.process?.entity_id}?children=100`) - .expect(200); - expect(body.children.childNodes.length).to.be(3); - expect(body.children.childNodes[0].entityID).to.be(infoEvent.process?.entity_id); - expect(body.children.childNodes[1].entityID).to.be(startEvent.process?.entity_id); - expect(body.children.childNodes[2].entityID).to.be(execEvent.process?.entity_id); - }); - }); - - describe('duplicate process running events', () => { - let origin: Event; - let startEvent: Event; - let infoEvent: Event; - let execEvent: Event; - let genData: InsertedEvents; - - before(async () => { - // Construct the following tree: - // Origin -> (infoEvent, startEvent, execEvent are all for the same node) - origin = generator.generateEvent({ - eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), - }); - startEvent = generator.generateEvent({ - parentEntityID: entityIDSafeVersion(origin), - ancestry: createAncestryArray([origin]), - eventType: ['start'], - eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), - }); - - infoEvent = generator.generateEvent({ - timestamp: (timestampSafeVersion(startEvent) ?? 0) + 100, - parentEntityID: entityIDSafeVersion(origin), - ancestry: createAncestryArray([origin]), - entityID: entityIDSafeVersion(startEvent), - eventType: ['info'], - eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), - }); - - execEvent = generator.generateEvent({ - timestamp: (timestampSafeVersion(infoEvent) ?? 0) + 100, - parentEntityID: entityIDSafeVersion(origin), - ancestry: createAncestryArray([origin]), - eventType: ['change'], - entityID: entityIDSafeVersion(startEvent), - eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), - }); - genData = await resolver.insertEvents( - [origin, infoEvent, startEvent, execEvent], - processEventsIndex - ); - }); - - after(async () => { - await resolver.deleteData(genData); - }); - - it('only retrieves the start event for the child node', async () => { - const childrenQuery = new ChildrenQuery( - ChildrenPaginationBuilder.createBuilder(100), - eventsIndexPattern - ); - // [1] here gets the body portion of the array - const [, query] = childrenQuery.buildMSearch(entityIDSafeVersion(origin) ?? ''); - const { body } = await es.search>({ body: query }); - expect(body.hits.hits.length).to.be(1); - - const event = body.hits.hits[0]._source; - expect(entityIDSafeVersion(event)).to.be(startEvent.process?.entity_id); - expect(event.event?.type).to.eql(['start']); - }); - }); - - describe('children api returns same node multiple times', () => { - let origin: Event; - let startEvent: Event; - let infoEvent: Event; - let execEvent: Event; - let genData: InsertedEvents; - - before(async () => { - // Construct the following tree: - // Origin -> (infoEvent, startEvent, execEvent are all for the same node) - origin = generator.generateEvent({ - eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), - }); - startEvent = generator.generateEvent({ - parentEntityID: entityIDSafeVersion(origin), - ancestry: createAncestryArray([origin]), - eventType: ['start'], - eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), - }); - - infoEvent = generator.generateEvent({ - timestamp: (timestampSafeVersion(startEvent) ?? 0) + 100, - parentEntityID: entityIDSafeVersion(origin), - ancestry: createAncestryArray([origin]), - entityID: entityIDSafeVersion(startEvent), - eventType: ['info'], - eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), - }); - - execEvent = generator.generateEvent({ - timestamp: (timestampSafeVersion(infoEvent) ?? 0) + 100, - parentEntityID: entityIDSafeVersion(origin), - ancestry: createAncestryArray([origin]), - eventType: ['change'], - entityID: entityIDSafeVersion(startEvent), - eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), - }); - genData = await resolver.insertEvents( - [origin, infoEvent, startEvent, execEvent], - processEventsIndex - ); - }); - - after(async () => { - await resolver.deleteData(genData); - }); - - it('retrieves the same node three times', async () => { - let { body }: { body: SafeResolverChildren } = await supertest - .get(`/api/endpoint/resolver/${origin.process?.entity_id}/children?children=1`) - .expect(200); - expect(body.childNodes.length).to.be(1); - expect(body.nextChild).to.not.be(null); - expect(body.childNodes[0].entityID).to.be(startEvent.process?.entity_id); - expect(body.childNodes[0].lifecycle[0].event?.type).to.eql(startEvent.event?.type); - - ({ body } = await supertest - .get( - `/api/endpoint/resolver/${origin.process?.entity_id}/children?children=1&afterChild=${body.nextChild}` - ) - .expect(200)); - expect(body.childNodes.length).to.be(1); - expect(body.nextChild).to.not.be(null); - expect(body.childNodes[0].entityID).to.be(infoEvent.process?.entity_id); - expect(body.childNodes[0].lifecycle[1].event?.type).to.eql(infoEvent.event?.type); - - ({ body } = await supertest - .get( - `/api/endpoint/resolver/${origin.process?.entity_id}/children?children=1&afterChild=${body.nextChild}` - ) - .expect(200)); - expect(body.childNodes.length).to.be(1); - expect(body.nextChild).to.not.be(null); - expect(body.childNodes[0].entityID).to.be(infoEvent.process?.entity_id); - expect(body.childNodes[0].lifecycle[2].event?.type).to.eql(execEvent.event?.type); - - ({ body } = await supertest - .get( - `/api/endpoint/resolver/${origin.process?.entity_id}/children?children=1&afterChild=${body.nextChild}` - ) - .expect(200)); - expect(body.childNodes.length).to.be(0); - expect(body.nextChild).to.be(null); - }); - }); - }); -} diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/common.ts b/x-pack/test/security_solution_endpoint_api_int/apis/resolver/common.ts index 3cc833c6a2475..7d2ac7e988169 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/common.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/resolver/common.ts @@ -8,10 +8,7 @@ import expect from '@kbn/expect'; import { firstNonNullValue } from '../../../../plugins/security_solution/common/endpoint/models/ecs_safety_helpers'; import { NodeID } from '../../../../plugins/security_solution/server/endpoint/routes/resolver/tree/utils'; import { - SafeResolverChildNode, - SafeResolverLifecycleNode, SafeResolverEvent, - ResolverNodeStats, ResolverNode, ResolverSchema, } from '../../../../plugins/security_solution/common/endpoint/types'; @@ -30,6 +27,32 @@ import { categoryMapping, } from '../../../../plugins/security_solution/common/endpoint/generate_data'; +/** + * Schema used for the /tree api post requests that uses the ancestry + */ +export const schemaWithAncestry: ResolverSchema = { + ancestry: 'process.Ext.ancestry', + id: 'process.entity_id', + parent: 'process.parent.entity_id', +}; + +/** + * Schema used for the /tree api post requests that does not use the ancestry + */ +export const schemaWithoutAncestry: ResolverSchema = { + id: 'process.entity_id', + parent: 'process.parent.entity_id', +}; + +/** + * Schema used for the /tree api post requests that uses the name field + */ +export const schemaWithName: ResolverSchema = { + id: 'process.entity_id', + parent: 'process.parent.entity_id', + name: 'process.name', +}; + const createLevels = ({ descendantsByParent, levels, @@ -388,138 +411,6 @@ export const createAncestryArray = (events: Event[]) => { return ancestry; }; -/** - * Check that the given lifecycle is in the resolver tree's corresponding map - * - * @deprecated use verifyTree - * @param node a lifecycle node containing the start and end events for a node - * @param nodeMap a map of entity_ids to nodes to look for the passed in `node` - */ -const expectLifecycleNodeInMap = ( - node: SafeResolverLifecycleNode, - nodeMap: Map -) => { - const genNode = nodeMap.get(node.entityID); - expect(genNode).to.be.ok(); - compareArrays(genNode!.lifecycle, node.lifecycle, true); -}; - -/** - * Verify that all the ancestor nodes are valid and optionally have parents. - * - * @deprecated use verifyTree - * @param ancestors an array of ancestors - * @param tree the generated resolver tree as the source of truth - * @param verifyLastParent a boolean indicating whether to check the last ancestor. If the ancestors array intentionally - * does not contain all the ancestors, the last one will not have the parent - */ -export const checkAncestryFromEntityTreeAPI = ( - ancestors: SafeResolverLifecycleNode[], - tree: Tree, - verifyLastParent: boolean -) => { - // group the ancestors by their entity_id mapped to a lifecycle node - const groupedAncestors = _.groupBy(ancestors, (ancestor) => ancestor.entityID); - // group by parent entity_id - const groupedAncestorsParent = _.groupBy(ancestors, (ancestor) => - parentEntityIDSafeVersion(ancestor.lifecycle[0]) - ); - // make sure there aren't any nodes with the same entity_id - expect(Object.keys(groupedAncestors).length).to.eql(ancestors.length); - // make sure there aren't any nodes with the same parent entity_id - expect(Object.keys(groupedAncestorsParent).length).to.eql(ancestors.length); - - // make sure each of the ancestors' lifecycle events are in the generated tree - for (const node of ancestors) { - expectLifecycleNodeInMap(node, tree.ancestry); - } - - // start at the origin which is always the first element of the array and make sure we have a connection - // using parent id between each of the nodes - let foundParents = 0; - let node = ancestors[0]; - for (let i = 0; i < ancestors.length; i++) { - const parentID = parentEntityIDSafeVersion(node.lifecycle[0]); - if (parentID !== undefined) { - const nextNode = groupedAncestors[parentID]; - if (!nextNode) { - break; - } - // the grouped nodes should only have a single entry since each entity is unique - node = nextNode[0]; - } - foundParents++; - } - - if (verifyLastParent) { - expect(foundParents).to.eql(ancestors.length); - } else { - // if we only retrieved a portion of all the ancestors then the most distant grandparent's parent will not necessarily - // be in the results - expect(foundParents).to.eql(ancestors.length - 1); - } -}; - -/** - * Retrieves the most distant ancestor in the given array. - * - * @deprecated use verifyTree - * @param ancestors an array of ancestor nodes - */ -export const retrieveDistantAncestor = (ancestors: SafeResolverLifecycleNode[]) => { - // group the ancestors by their entity_id mapped to a lifecycle node - const groupedAncestors = _.groupBy(ancestors, (ancestor) => ancestor.entityID); - let node = ancestors[0]; - for (let i = 0; i < ancestors.length; i++) { - const parentID = parentEntityIDSafeVersion(node.lifecycle[0]); - if (parentID !== undefined) { - const nextNode = groupedAncestors[parentID]; - if (nextNode) { - node = nextNode[0]; - } else { - return node; - } - } - } - return node; -}; - -/** - * Verify that the children nodes are correct - * - * @deprecated use verifyTree - * @param children the children nodes - * @param tree the generated resolver tree as the source of truth - * @param numberOfParents an optional number to compare that are a certain number of parents in the children array - * @param childrenPerParent an optional number to compare that there are a certain number of children for each parent - */ -export const verifyChildrenFromEntityTreeAPI = ( - children: SafeResolverChildNode[], - tree: Tree, - numberOfParents?: number, - childrenPerParent?: number -) => { - // group the children by their entity_id mapped to a child node - const groupedChildren = _.groupBy(children, (child) => child.entityID); - // make sure each child is unique - expect(Object.keys(groupedChildren).length).to.eql(children.length); - if (numberOfParents !== undefined) { - const groupParent = _.groupBy(children, (child) => - parentEntityIDSafeVersion(child.lifecycle[0]) - ); - expect(Object.keys(groupParent).length).to.eql(numberOfParents); - if (childrenPerParent !== undefined) { - Object.values(groupParent).forEach((childNodes) => - expect(childNodes.length).to.be(childrenPerParent) - ); - } - } - - children.forEach((child) => { - expectLifecycleNodeInMap(child, tree.children); - }); -}; - /** * Compare an array of events returned from an API with an array of events generated * @@ -547,51 +438,3 @@ export const compareArrays = ( ).to.be.ok(); }); }; - -/** - * Verifies that the stats received from ES for a node reflect the categories of events that the generator created. - * - * @deprecated use verifyTree - * @param relatedEvents the related events received for a particular node - * @param categories the related event info used when generating the resolver tree - */ -export const verifyEntityTreeStats = ( - stats: ResolverNodeStats | undefined, - categories: RelatedEventInfo[], - relatedAlerts: number -) => { - expect(stats).to.not.be(undefined); - let totalExpEvents = 0; - for (const cat of categories) { - const ecsCategories = categoryMapping[cat.category]; - if (Array.isArray(ecsCategories)) { - // if there are multiple ecs categories used to define a related event, the count for all of them should be the same - // and they should equal what is defined in the categories used to generate the related events - for (const ecsCat of ecsCategories) { - expect(stats?.events.byCategory[ecsCat]).to.be(cat.count); - } - } else { - expect(stats?.events.byCategory[ecsCategories]).to.be(cat.count); - } - - totalExpEvents += cat.count; - } - expect(stats?.events.total).to.be(totalExpEvents); -}; - -/** - * A helper function for verifying the stats information an array of nodes. - * - * @deprecated use verifyTree - * @param nodes an array of lifecycle nodes that should have a stats field defined - * @param categories the related event info used when generating the resolver tree - */ -export const verifyLifecycleStats = ( - nodes: SafeResolverLifecycleNode[], - categories: RelatedEventInfo[], - relatedAlerts: number -) => { - for (const node of nodes) { - verifyEntityTreeStats(node.stats, categories, relatedAlerts); - } -}; diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/entity_id.ts b/x-pack/test/security_solution_endpoint_api_int/apis/resolver/entity_id.ts index f9492e6291684..98c9e09de5587 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/entity_id.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/resolver/entity_id.ts @@ -4,11 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ import expect from '@kbn/expect'; -import { entityIDSafeVersion } from '../../../../plugins/security_solution/common/endpoint/models/event'; +import { + entityIDSafeVersion, + timestampAsDateSafeVersion, +} from '../../../../plugins/security_solution/common/endpoint/models/event'; import { eventsIndexPattern } from '../../../../plugins/security_solution/common/endpoint/constants'; import { - SafeResolverTree, ResolverEntityIndex, + ResolverNode, } from '../../../../plugins/security_solution/common/endpoint/types'; import { FtrProviderContext } from '../../ftr_provider_context'; import { @@ -16,7 +19,7 @@ import { Event, } from '../../../../plugins/security_solution/common/endpoint/generate_data'; import { InsertedEvents, processEventsIndex } from '../../services/resolver'; -import { createAncestryArray } from './common'; +import { createAncestryArray, schemaWithAncestry } from './common'; export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); @@ -93,11 +96,23 @@ export default function ({ getService }: FtrProviderContext) { }); it('does not find children without a process entity_id', async () => { - const { body }: { body: SafeResolverTree } = await supertest - .get(`/api/endpoint/resolver/${origin.process?.entity_id}`) + const { body }: { body: ResolverNode[] } = await supertest + .post('/api/endpoint/resolver/tree') + .set('kbn-xsrf', 'xxx') + .send({ + descendants: 100, + ancestors: 0, + schema: schemaWithAncestry, + nodes: [origin.process?.entity_id], + timeRange: { + from: timestampAsDateSafeVersion(origin)?.toISOString(), + to: timestampAsDateSafeVersion(childWithEntityID)?.toISOString(), + }, + indexPatterns: ['logs-*'], + }) .expect(200); - expect(body.children.childNodes.length).to.be(1); - expect(body.children.childNodes[0].entityID).to.be(childWithEntityID.process?.entity_id); + expect(body.length).to.be(1); + expect(body[0].id).to.be(childWithEntityID.process?.entity_id); }); }); @@ -147,11 +162,32 @@ export default function ({ getService }: FtrProviderContext) { }); it('does not query for ancestors that have an empty string for the entity_id', async () => { - const { body }: { body: SafeResolverTree } = await supertest - .get(`/api/endpoint/resolver/${origin.process?.entity_id}`) + const hasID = (nodes: ResolverNode[], id: string | undefined): boolean => { + if (!id) { + return false; + } + return nodes.find((node) => node.id === id) !== undefined; + }; + const { body }: { body: ResolverNode[] } = await supertest + .post('/api/endpoint/resolver/tree') + .set('kbn-xsrf', 'xxx') + .send({ + descendants: 0, + ancestors: 10, + schema: schemaWithAncestry, + nodes: [origin.process?.entity_id], + timeRange: { + from: timestampAsDateSafeVersion(ancestor2)?.toISOString(), + to: timestampAsDateSafeVersion(origin)?.toISOString(), + }, + indexPatterns: ['logs-*'], + }) .expect(200); - expect(body.ancestry.ancestors.length).to.be(1); - expect(body.ancestry.ancestors[0].entityID).to.be(ancestor2.process?.entity_id); + // the origin itself will be returned as part of the /tree request + // and it's single valid ancestor + expect(body.length).to.be(2); + expect(hasID(body, entityIDSafeVersion(origin))).to.be(true); + expect(hasID(body, entityIDSafeVersion(ancestor2))).to.be(true); }); }); }); diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/index.ts b/x-pack/test/security_solution_endpoint_api_int/apis/resolver/index.ts index 0ba5460f09d9d..c6bdb9a183c4c 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/index.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/resolver/index.ts @@ -11,10 +11,7 @@ export default function (providerContext: FtrProviderContext) { describe('Resolver tests', () => { loadTestFile(require.resolve('./entity_id')); loadTestFile(require.resolve('./entity')); - loadTestFile(require.resolve('./children')); - loadTestFile(require.resolve('./tree_entity_id')); loadTestFile(require.resolve('./tree')); - loadTestFile(require.resolve('./alerts')); loadTestFile(require.resolve('./events')); }); } diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/tree.ts b/x-pack/test/security_solution_endpoint_api_int/apis/resolver/tree.ts index ab6cac7f357a0..5cb37cad10491 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/tree.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/resolver/tree.ts @@ -5,10 +5,7 @@ */ import expect from '@kbn/expect'; import { getNameField } from '../../../../plugins/security_solution/server/endpoint/routes/resolver/tree/utils/fetch'; -import { - ResolverNode, - ResolverSchema, -} from '../../../../plugins/security_solution/common/endpoint/types'; +import { ResolverNode } from '../../../../plugins/security_solution/common/endpoint/types'; import { parentEntityIDSafeVersion, timestampSafeVersion, @@ -19,7 +16,7 @@ import { RelatedEventCategory, } from '../../../../plugins/security_solution/common/endpoint/generate_data'; import { Options, GeneratedTrees } from '../../services/resolver'; -import { verifyTree } from './common'; +import { schemaWithAncestry, schemaWithName, schemaWithoutAncestry, verifyTree } from './common'; export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); @@ -46,23 +43,6 @@ export default function ({ getService }: FtrProviderContext) { ancestryArraySize: 2, }; - const schemaWithAncestry: ResolverSchema = { - ancestry: 'process.Ext.ancestry', - id: 'process.entity_id', - parent: 'process.parent.entity_id', - }; - - const schemaWithoutAncestry: ResolverSchema = { - id: 'process.entity_id', - parent: 'process.parent.entity_id', - }; - - const schemaWithName: ResolverSchema = { - id: 'process.entity_id', - parent: 'process.parent.entity_id', - name: 'process.name', - }; - describe('Resolver tree', () => { before(async () => { resolverTrees = await resolver.createTrees(treeOptions); diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/tree_entity_id.ts b/x-pack/test/security_solution_endpoint_api_int/apis/resolver/tree_entity_id.ts deleted file mode 100644 index 39cce77b8cc9d..0000000000000 --- a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/tree_entity_id.ts +++ /dev/null @@ -1,375 +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; - * you may not use this file except in compliance with the Elastic License. - */ -import expect from '@kbn/expect'; -import { - SafeResolverAncestry, - SafeResolverChildren, - SafeResolverTree, - SafeLegacyEndpointEvent, -} from '../../../../plugins/security_solution/common/endpoint/types'; -import { parentEntityIDSafeVersion } from '../../../../plugins/security_solution/common/endpoint/models/event'; -import { FtrProviderContext } from '../../ftr_provider_context'; -import { - Tree, - RelatedEventCategory, -} from '../../../../plugins/security_solution/common/endpoint/generate_data'; -import { Options, GeneratedTrees } from '../../services/resolver'; -import { - compareArrays, - checkAncestryFromEntityTreeAPI, - retrieveDistantAncestor, - verifyChildrenFromEntityTreeAPI, - verifyLifecycleStats, - verifyEntityTreeStats, -} from './common'; - -export default function ({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); - const resolver = getService('resolverGenerator'); - - const relatedEventsToGen = [ - { category: RelatedEventCategory.Driver, count: 2 }, - { category: RelatedEventCategory.File, count: 1 }, - { category: RelatedEventCategory.Registry, count: 1 }, - ]; - const relatedAlerts = 4; - let resolverTrees: GeneratedTrees; - let tree: Tree; - const treeOptions: Options = { - ancestors: 5, - relatedEvents: relatedEventsToGen, - relatedAlerts, - children: 3, - generations: 2, - percentTerminated: 100, - percentWithRelated: 100, - numTrees: 1, - alwaysGenMaxChildrenPerNode: true, - ancestryArraySize: 2, - }; - - describe('Resolver entity tree api', () => { - before(async () => { - await esArchiver.load('endpoint/resolver/api_feature'); - resolverTrees = await resolver.createTrees(treeOptions); - // we only requested a single alert so there's only 1 tree - tree = resolverTrees.trees[0]; - }); - after(async () => { - await resolver.deleteData(resolverTrees); - // this unload is for an endgame-* index so it does not use data streams - await esArchiver.unload('endpoint/resolver/api_feature'); - }); - - describe('ancestry events route', () => { - describe('legacy events', () => { - const endpointID = '5a0c957f-b8e7-4538-965e-57e8bb86ad3a'; - const entityID = '94042'; - - it('should return details for the root node', async () => { - const { body }: { body: SafeResolverAncestry } = await supertest - .get( - `/api/endpoint/resolver/${entityID}/ancestry?legacyEndpointID=${endpointID}&ancestors=5` - ) - .expect(200); - expect(body.ancestors[0].lifecycle.length).to.eql(2); - expect(body.ancestors.length).to.eql(2); - expect(body.nextAncestor).to.eql(null); - }); - - it('should have a populated next parameter', async () => { - const { body }: { body: SafeResolverAncestry } = await supertest - .get( - `/api/endpoint/resolver/${entityID}/ancestry?legacyEndpointID=${endpointID}&ancestors=0` - ) - .expect(200); - expect(body.nextAncestor).to.eql('94041'); - }); - - it('should handle an ancestors param request', async () => { - let { body }: { body: SafeResolverAncestry } = await supertest - .get( - `/api/endpoint/resolver/${entityID}/ancestry?legacyEndpointID=${endpointID}&ancestors=0` - ) - .expect(200); - const next = body.nextAncestor; - - ({ body } = await supertest - .get( - `/api/endpoint/resolver/${next}/ancestry?legacyEndpointID=${endpointID}&ancestors=1` - ) - .expect(200)); - expect(body.ancestors[0].lifecycle.length).to.eql(1); - expect(body.nextAncestor).to.eql(null); - }); - }); - - describe('endpoint events', () => { - it('should return the origin node at the front of the array', async () => { - const { body }: { body: SafeResolverAncestry } = await supertest - .get(`/api/endpoint/resolver/${tree.origin.id}/ancestry?ancestors=9`) - .expect(200); - expect(body.ancestors[0].entityID).to.eql(tree.origin.id); - }); - - it('should return details for the root node', async () => { - const { body }: { body: SafeResolverAncestry } = await supertest - .get(`/api/endpoint/resolver/${tree.origin.id}/ancestry?ancestors=9`) - .expect(200); - // the tree we generated had 5 ancestors + 1 origin node - expect(body.ancestors.length).to.eql(6); - expect(body.ancestors[0].entityID).to.eql(tree.origin.id); - checkAncestryFromEntityTreeAPI(body.ancestors, tree, true); - expect(body.nextAncestor).to.eql(null); - }); - - it('should handle an invalid id', async () => { - const { body }: { body: SafeResolverAncestry } = await supertest - .get(`/api/endpoint/resolver/alskdjflasj/ancestry`) - .expect(200); - expect(body.ancestors).to.be.empty(); - expect(body.nextAncestor).to.eql(null); - }); - - it('should have a populated next parameter', async () => { - const { body }: { body: SafeResolverAncestry } = await supertest - .get(`/api/endpoint/resolver/${tree.origin.id}/ancestry?ancestors=2`) - .expect(200); - // it should have 2 ancestors + 1 origin - expect(body.ancestors.length).to.eql(3); - checkAncestryFromEntityTreeAPI(body.ancestors, tree, false); - const distantGrandparent = retrieveDistantAncestor(body.ancestors); - expect(body.nextAncestor).to.eql( - parentEntityIDSafeVersion(distantGrandparent.lifecycle[0]) - ); - }); - - it('should handle multiple ancestor requests', async () => { - let { body }: { body: SafeResolverAncestry } = await supertest - .get(`/api/endpoint/resolver/${tree.origin.id}/ancestry?ancestors=3`) - .expect(200); - expect(body.ancestors.length).to.eql(4); - const next = body.nextAncestor; - - ({ body } = await supertest - .get(`/api/endpoint/resolver/${next}/ancestry?ancestors=1`) - .expect(200)); - expect(body.ancestors.length).to.eql(2); - checkAncestryFromEntityTreeAPI(body.ancestors, tree, true); - // the highest node in the generated tree will not have a parent ID which causes the server to return - // without setting the pagination so nextAncestor will be null - expect(body.nextAncestor).to.eql(null); - }); - }); - }); - - describe('children route', () => { - describe('legacy events', () => { - const endpointID = '5a0c957f-b8e7-4538-965e-57e8bb86ad3a'; - const entityID = '94041'; - - it('returns child process lifecycle events', async () => { - const { body }: { body: SafeResolverChildren } = await supertest - .get(`/api/endpoint/resolver/${entityID}/children?legacyEndpointID=${endpointID}`) - .expect(200); - expect(body.childNodes.length).to.eql(1); - expect(body.childNodes[0].lifecycle.length).to.eql(2); - expect( - // for some reason the ts server doesn't think `endgame` exists even though we're using ResolverEvent - // here, so to avoid it complaining we'll just force it - (body.childNodes[0].lifecycle[0] as SafeLegacyEndpointEvent).endgame.unique_pid - ).to.eql(94042); - }); - - it('returns multiple levels of child process lifecycle events', async () => { - const { body }: { body: SafeResolverChildren } = await supertest - .get(`/api/endpoint/resolver/93802/children?legacyEndpointID=${endpointID}&children=10`) - .expect(200); - expect(body.childNodes.length).to.eql(10); - expect(body.nextChild).to.be(null); - expect(body.childNodes[0].lifecycle.length).to.eql(1); - expect( - // for some reason the ts server doesn't think `endgame` exists even though we're using ResolverEvent - // here, so to avoid it complaining we'll just force it - (body.childNodes[0].lifecycle[0] as SafeLegacyEndpointEvent).endgame.unique_pid - ).to.eql(93932); - }); - - it('returns no values when there is no more data', async () => { - let { body }: { body: SafeResolverChildren } = await supertest - .get( - // there should only be a single child for this node - `/api/endpoint/resolver/94041/children?legacyEndpointID=${endpointID}&children=1` - ) - .expect(200); - expect(body.nextChild).to.not.be(null); - - ({ body } = await supertest - .get( - `/api/endpoint/resolver/94041/children?legacyEndpointID=${endpointID}&afterChild=${body.nextChild}` - ) - .expect(200)); - expect(body.childNodes).be.empty(); - expect(body.nextChild).to.eql(null); - }); - - it('returns the first page of information when the cursor is invalid', async () => { - const { body }: { body: SafeResolverChildren } = await supertest - .get( - `/api/endpoint/resolver/${entityID}/children?legacyEndpointID=${endpointID}&afterChild=blah` - ) - .expect(200); - expect(body.childNodes.length).to.eql(1); - expect(body.nextChild).to.be(null); - }); - - it('errors on invalid pagination values', async () => { - await supertest.get(`/api/endpoint/resolver/${entityID}/children?children=0`).expect(400); - await supertest - .get(`/api/endpoint/resolver/${entityID}/children?children=20000`) - .expect(400); - await supertest - .get(`/api/endpoint/resolver/${entityID}/children?children=-1`) - .expect(400); - }); - - it('returns empty events without a matching entity id', async () => { - const { body }: { body: SafeResolverChildren } = await supertest - .get(`/api/endpoint/resolver/5555/children`) - .expect(200); - expect(body.nextChild).to.eql(null); - expect(body.childNodes).to.be.empty(); - }); - - it('returns empty events with an invalid endpoint id', async () => { - const { body }: { body: SafeResolverChildren } = await supertest - .get(`/api/endpoint/resolver/${entityID}/children?legacyEndpointID=foo`) - .expect(200); - expect(body.nextChild).to.eql(null); - expect(body.childNodes).to.be.empty(); - }); - }); - - describe('endpoint events', () => { - it('returns all children for the origin', async () => { - const { body }: { body: SafeResolverChildren } = await supertest - .get(`/api/endpoint/resolver/${tree.origin.id}/children?children=100`) - .expect(200); - // there are 2 levels in the children part of the tree and 3 nodes for each = - // 3 children for the origin + 3 children for each of the origin's children = 12 - expect(body.childNodes.length).to.eql(12); - // there will be 4 parents, the origin of the tree, and it's 3 children - verifyChildrenFromEntityTreeAPI(body.childNodes, tree, 4, 3); - expect(body.nextChild).to.eql(null); - }); - - it('returns a single generation of children', async () => { - // this gets a node should have 3 children which were created in succession so that the timestamps - // are ordered correctly to be retrieved in a single call - const distantChildEntityID = Array.from(tree.childrenLevels[0].values())[0].id; - const { body }: { body: SafeResolverChildren } = await supertest - .get(`/api/endpoint/resolver/${distantChildEntityID}/children?children=3`) - .expect(200); - expect(body.childNodes.length).to.eql(3); - verifyChildrenFromEntityTreeAPI(body.childNodes, tree, 1, 3); - expect(body.nextChild).to.not.eql(null); - }); - - it('paginates the children', async () => { - // this gets a node should have 3 children which were created in succession so that the timestamps - // are ordered correctly to be retrieved in a single call - const distantChildEntityID = Array.from(tree.childrenLevels[0].values())[0].id; - let { body }: { body: SafeResolverChildren } = await supertest - .get(`/api/endpoint/resolver/${distantChildEntityID}/children?children=1`) - .expect(200); - expect(body.childNodes.length).to.eql(1); - verifyChildrenFromEntityTreeAPI(body.childNodes, tree, 1, 1); - expect(body.nextChild).to.not.be(null); - - ({ body } = await supertest - .get( - `/api/endpoint/resolver/${distantChildEntityID}/children?children=2&afterChild=${body.nextChild}` - ) - .expect(200)); - expect(body.childNodes.length).to.eql(2); - verifyChildrenFromEntityTreeAPI(body.childNodes, tree, 1, 2); - expect(body.nextChild).to.not.be(null); - - ({ body } = await supertest - .get( - `/api/endpoint/resolver/${distantChildEntityID}/children?children=2&afterChild=${body.nextChild}` - ) - .expect(200)); - expect(body.childNodes.length).to.eql(0); - expect(body.nextChild).to.be(null); - }); - - it('gets all children in two queries', async () => { - // should get all the children of the origin - let { body }: { body: SafeResolverChildren } = await supertest - .get(`/api/endpoint/resolver/${tree.origin.id}/children?children=3`) - .expect(200); - expect(body.childNodes.length).to.eql(3); - verifyChildrenFromEntityTreeAPI(body.childNodes, tree); - expect(body.nextChild).to.not.be(null); - const firstNodes = [...body.childNodes]; - - ({ body } = await supertest - .get( - `/api/endpoint/resolver/${tree.origin.id}/children?children=10&afterChild=${body.nextChild}` - ) - .expect(200)); - expect(body.childNodes.length).to.eql(9); - // put all the results together and we should have all the children - verifyChildrenFromEntityTreeAPI([...firstNodes, ...body.childNodes], tree, 4, 3); - expect(body.nextChild).to.be(null); - }); - }); - }); - - describe('tree api', () => { - describe('legacy events', () => { - const endpointID = '5a0c957f-b8e7-4538-965e-57e8bb86ad3a'; - - it('returns ancestors, events, children, and current process lifecycle', async () => { - const { body }: { body: SafeResolverTree } = await supertest - .get(`/api/endpoint/resolver/93933?legacyEndpointID=${endpointID}`) - .expect(200); - expect(body.ancestry.nextAncestor).to.equal(null); - expect(body.children.nextChild).to.equal(null); - expect(body.children.childNodes.length).to.equal(0); - expect(body.lifecycle.length).to.equal(2); - }); - }); - - describe('endpoint events', () => { - it('returns a tree', async () => { - const { body }: { body: SafeResolverTree } = await supertest - .get( - `/api/endpoint/resolver/${tree.origin.id}?children=100&ancestors=5&events=5&alerts=5` - ) - .expect(200); - - expect(body.children.nextChild).to.equal(null); - expect(body.children.childNodes.length).to.equal(12); - verifyChildrenFromEntityTreeAPI(body.children.childNodes, tree, 4, 3); - verifyLifecycleStats(body.children.childNodes, relatedEventsToGen, relatedAlerts); - - expect(body.ancestry.nextAncestor).to.equal(null); - checkAncestryFromEntityTreeAPI(body.ancestry.ancestors, tree, true); - verifyLifecycleStats(body.ancestry.ancestors, relatedEventsToGen, relatedAlerts); - - expect(body.relatedAlerts.nextAlert).to.equal(null); - compareArrays(tree.origin.relatedAlerts, body.relatedAlerts.alerts, true); - - compareArrays(tree.origin.lifecycle, body.lifecycle, true); - verifyEntityTreeStats(body.stats, relatedEventsToGen, relatedAlerts); - }); - }); - }); - }); -} diff --git a/x-pack/test/send_search_to_background_integration/config.ts b/x-pack/test/send_search_to_background_integration/config.ts index 7dd0915de3c33..957c183d04536 100644 --- a/x-pack/test/send_search_to_background_integration/config.ts +++ b/x-pack/test/send_search_to_background_integration/config.ts @@ -20,7 +20,10 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { reportName: 'X-Pack Background Search UI (Enabled WIP Feature)', }, - testFiles: [resolve(__dirname, './tests/apps/dashboard/async_search')], + testFiles: [ + resolve(__dirname, './tests/apps/dashboard/async_search'), + resolve(__dirname, './tests/apps/discover'), + ], kbnTestServer: { ...xpackFunctionalConfig.get('kbnTestServer'), diff --git a/x-pack/test/send_search_to_background_integration/tests/apps/dashboard/async_search/sessions_in_space.ts b/x-pack/test/send_search_to_background_integration/tests/apps/dashboard/async_search/sessions_in_space.ts index e2e0adf447afe..97fba9e3aef91 100644 --- a/x-pack/test/send_search_to_background_integration/tests/apps/dashboard/async_search/sessions_in_space.ts +++ b/x-pack/test/send_search_to_background_integration/tests/apps/dashboard/async_search/sessions_in_space.ts @@ -34,7 +34,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }, kibana: [ { - base: ['all'], + feature: { + dashboard: ['minimal_read', 'store_search_session'], + }, spaces: ['another-space'], }, ], @@ -54,6 +56,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); after(async () => { + await security.role.delete('data_analyst'); + await security.user.delete('analyst'); + await esArchiver.unload('dashboard/session_in_space'); await PageObjects.security.forceLogout(); }); diff --git a/x-pack/test/send_search_to_background_integration/tests/apps/discover/index.ts b/x-pack/test/send_search_to_background_integration/tests/apps/discover/index.ts new file mode 100644 index 0000000000000..b4fc74072ce00 --- /dev/null +++ b/x-pack/test/send_search_to_background_integration/tests/apps/discover/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ loadTestFile, getService }: FtrProviderContext) { + const kibanaServer = getService('kibanaServer'); + const esArchiver = getService('esArchiver'); + + describe('async search', function () { + this.tags('ciGroup3'); + + before(async () => { + await esArchiver.loadIfNeeded('logstash_functional'); + await kibanaServer.uiSettings.replace({ defaultIndex: 'logstash-*' }); + }); + + loadTestFile(require.resolve('./sessions_in_space')); + }); +} diff --git a/x-pack/test/send_search_to_background_integration/tests/apps/discover/sessions_in_space.ts b/x-pack/test/send_search_to_background_integration/tests/apps/discover/sessions_in_space.ts new file mode 100644 index 0000000000000..5c94a50e0a84d --- /dev/null +++ b/x-pack/test/send_search_to_background_integration/tests/apps/discover/sessions_in_space.ts @@ -0,0 +1,100 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + const esArchiver = getService('esArchiver'); + const security = getService('security'); + const inspector = getService('inspector'); + const PageObjects = getPageObjects([ + 'common', + 'header', + 'discover', + 'visChart', + 'security', + 'timePicker', + ]); + const browser = getService('browser'); + const sendToBackground = getService('sendToBackground'); + + describe('discover in space', () => { + describe('Send to background in space', () => { + before(async () => { + await esArchiver.load('dashboard/session_in_space'); + + await security.role.create('data_analyst', { + elasticsearch: { + indices: [{ names: ['logstash-*'], privileges: ['all'] }], + }, + kibana: [ + { + feature: { + discover: ['all'], + }, + spaces: ['another-space'], + }, + ], + }); + + await security.user.create('analyst', { + password: 'analyst-password', + roles: ['data_analyst'], + full_name: 'test user', + }); + + await PageObjects.security.forceLogout(); + + await PageObjects.security.login('analyst', 'analyst-password', { + expectSpaceSelector: false, + }); + }); + + after(async () => { + await security.role.delete('data_analyst'); + await security.user.delete('analyst'); + + await esArchiver.unload('dashboard/session_in_space'); + await PageObjects.security.forceLogout(); + }); + + it('Saves and restores a session', async () => { + await PageObjects.common.navigateToApp('discover', { basePath: 's/another-space' }); + + await PageObjects.discover.selectIndexPattern('logstash-*'); + + await PageObjects.timePicker.setAbsoluteRange( + 'Sep 1, 2015 @ 00:00:00.000', + 'Oct 1, 2015 @ 00:00:00.000' + ); + + await PageObjects.discover.waitForDocTableLoadingComplete(); + + await sendToBackground.expectState('completed'); + await sendToBackground.save(); + await sendToBackground.expectState('backgroundCompleted'); + await inspector.open(); + + const savedSessionId = await ( + await testSubjects.find('inspectorRequestSearchSessionId') + ).getAttribute('data-search-session-id'); + await inspector.close(); + + // load URL to restore a saved session + const url = await browser.getCurrentUrl(); + const savedSessionURL = `${url}&searchSessionId=${savedSessionId}`; + await browser.get(savedSessionURL); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.discover.waitForDocTableLoadingComplete(); + + // Check that session is restored + await sendToBackground.expectState('restored'); + await testSubjects.missingOrFail('embeddableErrorLabel'); + }); + }); + }); +} diff --git a/yarn.lock b/yarn.lock index 6e4df2f5b197a..b02ccc4ee436f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1411,10 +1411,10 @@ version "0.0.0" uid "" -"@elastic/elasticsearch@7.10.0": - version "7.10.0" - resolved "https://registry.yarnpkg.com/@elastic/elasticsearch/-/elasticsearch-7.10.0.tgz#da105a9c1f14146f9f2cab4e7026cb7949121b8d" - integrity sha512-vXtMAQf5/DwqeryQgRriMtnFppJNLc/R7/R0D8E+wG5/kGM5i7mg+Hi7TM4NZEuXgtzZ2a/Nf7aR0vLyrxOK/w== +"@elastic/elasticsearch@npm:@elastic/elasticsearch-canary@^8.0.0-canary": + version "8.0.0-canary.1" + resolved "https://registry.yarnpkg.com/@elastic/elasticsearch-canary/-/elasticsearch-canary-8.0.0-canary.1.tgz#5cd0eda62531b71af66a08da6c3cebc26a73d4c0" + integrity sha512-VhQ42wH+0OGmHSlc4It3bqGTL7mLuC2RIionJZBIuY5P6lwUMz7goelfyfTHoo+LStxz5QQ8Zt2xcnAnShTBJg== dependencies: debug "^4.1.1" hpagent "^0.1.1" @@ -1439,10 +1439,10 @@ resolved "https://registry.yarnpkg.com/@elastic/eslint-plugin-eui/-/eslint-plugin-eui-0.0.2.tgz#56b9ef03984a05cc213772ae3713ea8ef47b0314" integrity sha512-IoxURM5zraoQ7C8f+mJb9HYSENiZGgRVcG4tLQxE61yHNNRDXtGDWTZh8N1KIHcsqN1CEPETjuzBXkJYF/fDiQ== -"@elastic/eui@30.6.0": - version "30.6.0" - resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-30.6.0.tgz#6653223223f52407ac05303825d9bd08382df1d5" - integrity sha512-40Jiy54MpJAx3lD3NSZZLkMkVySwKpX6RxIKnvT3somE95pwIjXrWB688m2nL2g05y7kNhjrhwfdctVzNXZENA== +"@elastic/eui@31.0.0": + version "31.0.0" + resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-31.0.0.tgz#7d17386c04a0ad343d70c3652902fcd3f46ed337" + integrity sha512-oj63HpQQKg/Cgwz5B0ZBQCkcgZiEdQzBT9PbmEiR/VRz5P0WqJpgZPyIF7jiFaFlGP1a9hPjkUTo+ramWNCpiw== dependencies: "@types/chroma-js" "^2.0.0" "@types/lodash" "^4.14.160" @@ -1925,10 +1925,10 @@ "@hapi/hoek" "9.x.x" "@hapi/validate" "1.x.x" -"@hapi/hoek@9.x.x", "@hapi/hoek@^9.0.0", "@hapi/hoek@^9.0.4", "@hapi/hoek@^9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.1.0.tgz#6c9eafc78c1529248f8f4d92b0799a712b6052c6" - integrity sha512-i9YbZPN3QgfighY/1X1Pu118VUz2Fmmhd6b2n0/O8YVgGGfw0FbUYoA97k7FkpGJ+pLCFEDLUmAPPV4D1kpeFw== +"@hapi/hoek@9.x.x", "@hapi/hoek@^9.0.0", "@hapi/hoek@^9.0.4", "@hapi/hoek@^9.1.1": + version "9.1.1" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.1.1.tgz#9daf5745156fd84b8e9889a2dc721f0c58e894aa" + integrity sha512-CAEbWH7OIur6jEOzaai83jq3FmKmv4PmX1JYfs9IrYcGEVI/lyL1EXJGCj7eFVJ0bg5QR8LMxBlEtA+xKiLpFw== "@hapi/inert@^6.0.3": version "6.0.3" @@ -28340,7 +28340,14 @@ vega-event-selector@^2.0.6, vega-event-selector@~2.0.6: resolved "https://registry.yarnpkg.com/vega-event-selector/-/vega-event-selector-2.0.6.tgz#6beb00e066b78371dde1a0f40cb5e0bbaecfd8bc" integrity sha512-UwCu50Sqd8kNZ1X/XgiAY+QAyQUmGFAwyDu7y0T5fs6/TPQnDo/Bo346NgSgINBEhEKOAMY1Nd/rPOk4UEm/ew== -vega-expression@^3.0.0, vega-expression@~3.0.0: +vega-expression@^4.0.0, vega-expression@^4.0.1, vega-expression@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/vega-expression/-/vega-expression-4.0.1.tgz#c03e4fc68a00acac49557faa4e4ed6ac8a59c5fd" + integrity sha512-ZrDj0hP8NmrCpdLFf7Rd/xMUHGoSYsAOTaYp7uXZ2dkEH5x0uPy5laECMc8TiQvL8W+8IrN2HAWCMRthTSRe2Q== + dependencies: + vega-util "^1.16.0" + +vega-expression@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/vega-expression/-/vega-expression-3.0.0.tgz#39179d010b34c57513162bf1ab5a7bff4b31be91" integrity sha512-/ObjIOK94MB+ziTuh8HZt2eWlKUPT/piRJLal5tx5QL1sQbfRi++7lHKTaKMLXLqc4Xqp9/DewE3PqQ6tYzaUA== @@ -28367,27 +28374,27 @@ vega-format@^1.0.4, vega-format@~1.0.4: vega-time "^2.0.3" vega-util "^1.15.2" -vega-functions@^5.8.0, vega-functions@~5.8.0: - version "5.8.0" - resolved "https://registry.yarnpkg.com/vega-functions/-/vega-functions-5.8.0.tgz#48e02b0e5b14261cd445bda3c4721a18b02c810c" - integrity sha512-xaUqWZHEX+EuJuKfN0Biux3rrCHDEHmMbW7LHYyyEqguR0i6+zhtOSUEWmYqDfzB/+BlIwCk5Vif6q6/mzJxbQ== +vega-functions@^5.10.0, vega-functions@~5.10.0: + version "5.10.0" + resolved "https://registry.yarnpkg.com/vega-functions/-/vega-functions-5.10.0.tgz#3d384111f13b3b0dd38a4fca656c5ae54b66e158" + integrity sha512-1l28OxUwOj8FEvRU62Oz2hiTuDECrvx1DPU1qLebBKhlgaKbcCk3XyHrn1kUzhMKpXq+SFv5VPxchZP47ASSvQ== dependencies: d3-array "^2.7.1" d3-color "^2.0.0" d3-geo "^2.0.1" vega-dataflow "^5.7.3" - vega-expression "^3.0.0" + vega-expression "^4.0.1" vega-scale "^7.1.1" vega-scenegraph "^4.9.2" - vega-selections "^5.1.4" + vega-selections "^5.1.5" vega-statistics "^1.7.9" vega-time "^2.0.4" - vega-util "^1.15.2" + vega-util "^1.16.0" -vega-geo@~4.3.7: - version "4.3.7" - resolved "https://registry.yarnpkg.com/vega-geo/-/vega-geo-4.3.7.tgz#4220137458a17d422fa15705f24905ba2595ca40" - integrity sha512-5HC1D9Z/WYuM1Gmlk8PxuRKgeN8snNWsfKO4E9PTmR7wo7tuU/2SGlRoE27aTsgwMMpBIrpRbSgKtgh5l/fMUQ== +vega-geo@~4.3.8: + version "4.3.8" + resolved "https://registry.yarnpkg.com/vega-geo/-/vega-geo-4.3.8.tgz#5629d18327bb4f3700cdf05db4aced0a43abbf4a" + integrity sha512-fsGxV96Q/QRgPqOPtMBZdI+DneIiROKTG3YDZvGn0EdV16OG5LzFhbNgLT5GPzI+kTwgLpAsucBHklexlB4kfg== dependencies: d3-array "^2.7.1" d3-color "^2.0.0" @@ -28446,14 +28453,14 @@ vega-loader@^4.3.2, vega-loader@^4.3.3, vega-loader@~4.4.0: vega-format "^1.0.4" vega-util "^1.16.0" -vega-parser@~6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/vega-parser/-/vega-parser-6.1.0.tgz#485fb6fcd79d14b09efee340e2b55fb510e57e20" - integrity sha512-u14bHXV8vtcuMIJkMNoDAJ4Xu3lwKIkep+YEkPumWvlwl3fClWy26EAcwTneeM3rXu2F6ZJI6W3ddu/If8u13w== +vega-parser@~6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/vega-parser/-/vega-parser-6.1.2.tgz#7f25751177e38c3239560a9c427ded8d2ba617bb" + integrity sha512-aGyZrNzPrBruEb/WhemKDuDjQsIkMDGIgnSJci0b+9ZVxjyAzMl7UfGbiYorPiJlnIercjUJbMoFD6fCIf4gqQ== dependencies: vega-dataflow "^5.7.3" vega-event-selector "^2.0.6" - vega-functions "^5.8.0" + vega-functions "^5.10.0" vega-scale "^7.1.1" vega-util "^1.15.2" @@ -28511,12 +28518,12 @@ vega-schema-url-parser@^2.1.0: resolved "https://registry.yarnpkg.com/vega-schema-url-parser/-/vega-schema-url-parser-2.1.0.tgz#847f9cf9f1624f36f8a51abc1adb41ebc6673cb4" integrity sha512-JHT1PfOyVzOohj89uNunLPirs05Nf59isPT5gnwIkJph96rRgTIBJE7l7yLqndd7fLjr3P8JXHGAryRp74sCaQ== -vega-selections@^5.1.4: - version "5.1.4" - resolved "https://registry.yarnpkg.com/vega-selections/-/vega-selections-5.1.4.tgz#cc086fac5b4e646f9f1e000777f8786782d8516a" - integrity sha512-L7CHwcIjVf90GoW2tS2x5O496O5Joaerp5A1KM6VJ1uo4z6KfqxY6M/328a/uaAs0LC5qbQgXT3htFbtUrPW/A== +vega-selections@^5.1.5: + version "5.1.5" + resolved "https://registry.yarnpkg.com/vega-selections/-/vega-selections-5.1.5.tgz#c7662edf26c1cfb18623573b30590c9774348d1c" + integrity sha512-oRSsfkqYqA5xfEJqDpgnSDd+w0k6p6SGYisMD6rGXMxuPl0x0Uy6RvDr4nbEtB+dpWdoWEvgrsZVS6axyDNWvQ== dependencies: - vega-expression "^3.0.0" + vega-expression "^4.0.0" vega-util "^1.15.2" vega-spec-injector@^0.0.2: @@ -28558,10 +28565,10 @@ vega-transforms@~4.9.3: vega-time "^2.0.4" vega-util "^1.15.2" -vega-typings@~0.19.0: - version "0.19.1" - resolved "https://registry.yarnpkg.com/vega-typings/-/vega-typings-0.19.1.tgz#a53949143fa37721ae7bd146bbb9add5c78aca52" - integrity sha512-OSyNYwMJ8FayTTNU/gohprbt1EFQBpoiMPP9p2vqo1O9z45XVnotQ92jYHAhraI6gWiMIIfo4OjPbSe/GX7etg== +vega-typings@~0.19.2: + version "0.19.2" + resolved "https://registry.yarnpkg.com/vega-typings/-/vega-typings-0.19.2.tgz#374fc1020c1abb263a0be87de28d1a4bd0526c3f" + integrity sha512-YU/S9rDk4d+t4+4eTa9fzuw87PMNteeVtpcL51kUO8H7HvGaoW7ll8RHKLkR0NYBEGPRoFDKUxnoyMvhgjsdYw== dependencies: vega-util "^1.15.2" @@ -28579,16 +28586,16 @@ vega-view-transforms@~4.5.8: vega-scenegraph "^4.9.2" vega-util "^1.15.2" -vega-view@~5.9.0: - version "5.9.0" - resolved "https://registry.yarnpkg.com/vega-view/-/vega-view-5.9.0.tgz#ee6d5abd66d2503dec71e05e7ca8cf813465ae3f" - integrity sha512-HqRFuqO2OwoPHHK+CVt8vB8fu2L8GjQerLpmEpglWtCPDns5+gn5B6F7M8Ah8v24WlfqW7cLrY81t9OloPZOyw== +vega-view@~5.9.2: + version "5.9.2" + resolved "https://registry.yarnpkg.com/vega-view/-/vega-view-5.9.2.tgz#cb957e481a952abbe7b3a11aa2d58cc728f295e7" + integrity sha512-XAwKWyVjLClR3aCbTLCWdZj7aZozOULNg7078GxJIgVcBJOENCAidceI/H7JieyUZ96p3AiEHLQdWr167InBpg== dependencies: d3-array "^2.7.1" d3-timer "^2.0.0" vega-dataflow "^5.7.3" vega-format "^1.0.4" - vega-functions "^5.8.0" + vega-functions "^5.10.0" vega-runtime "^6.1.3" vega-scenegraph "^4.9.2" vega-util "^1.15.2" @@ -28613,24 +28620,24 @@ vega-wordcloud@~4.1.3: vega-statistics "^1.7.9" vega-util "^1.15.2" -vega@^5.17.0: - version "5.17.0" - resolved "https://registry.yarnpkg.com/vega/-/vega-5.17.0.tgz#2b33296e257c97b79ee6501d4d1905fb1414d080" - integrity sha512-2Rm9aS3cSMXE55YgjfkuOmvSBMtiM/85/qX/WHLc+YiJacKGiwY9yzeC+w2Ft50JUs3nKZc1KB90ePgf5mfo0Q== +vega@^5.17.3: + version "5.17.3" + resolved "https://registry.yarnpkg.com/vega/-/vega-5.17.3.tgz#9901f24c8cf5ff2e98f3fddb372b8f5a6d8502d8" + integrity sha512-c8N2pNg9MMmC6shNpoxVw3aVp2XPFOgmWNX5BEOAdCaGHRnSgzNy44+gYdGRaIe6+ljTzZg99Mf+OLO50IP42A== dependencies: vega-crossfilter "~4.0.5" vega-dataflow "~5.7.3" vega-encode "~4.8.3" vega-event-selector "~2.0.6" - vega-expression "~3.0.0" + vega-expression "~4.0.1" vega-force "~4.0.7" vega-format "~1.0.4" - vega-functions "~5.8.0" - vega-geo "~4.3.7" + vega-functions "~5.10.0" + vega-geo "~4.3.8" vega-hierarchy "~4.0.9" vega-label "~1.0.0" vega-loader "~4.4.0" - vega-parser "~6.1.0" + vega-parser "~6.1.2" vega-projection "~1.4.5" vega-regression "~1.0.9" vega-runtime "~6.1.3" @@ -28639,9 +28646,9 @@ vega@^5.17.0: vega-statistics "~1.7.9" vega-time "~2.0.4" vega-transforms "~4.9.3" - vega-typings "~0.19.0" + vega-typings "~0.19.2" vega-util "~1.16.0" - vega-view "~5.9.0" + vega-view "~5.9.2" vega-view-transforms "~4.5.8" vega-voronoi "~4.1.5" vega-wordcloud "~4.1.3"