diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 5fcb619af6570..c91d1a702b7ec 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -315,7 +315,6 @@ /src/plugins/es_ui_shared/ @elastic/kibana-stack-management /x-pack/plugins/cross_cluster_replication/ @elastic/kibana-stack-management /x-pack/plugins/index_lifecycle_management/ @elastic/kibana-stack-management -/x-pack/plugins/console_extensions/ @elastic/kibana-stack-management /x-pack/plugins/grokdebugger/ @elastic/kibana-stack-management /x-pack/plugins/index_management/ @elastic/kibana-stack-management /x-pack/plugins/license_api_guard/ @elastic/kibana-stack-management @@ -330,7 +329,6 @@ /x-pack/plugins/ingest_pipelines/ @elastic/kibana-stack-management /packages/kbn-ace/ @elastic/kibana-stack-management /packages/kbn-monaco/ @elastic/kibana-stack-management -#CC# /x-pack/plugins/console_extensions/ @elastic/kibana-stack-management #CC# /x-pack/plugins/cross_cluster_replication/ @elastic/kibana-stack-management # Security Solution diff --git a/.github/workflows/project-assigner.yml b/.github/workflows/project-assigner.yml index f4e62648a9741..f2359846504bf 100644 --- a/.github/workflows/project-assigner.yml +++ b/.github/workflows/project-assigner.yml @@ -14,6 +14,7 @@ jobs: issue-mappings: | [ {"label": "Feature:Lens", "projectNumber": 32, "columnName": "Long-term goals"}, + {"label": "Feature:Discover", "projectNumber": 44, "columnName": "Inbox"}, {"label": "Feature:Canvas", "projectNumber": 38, "columnName": "Inbox"}, {"label": "Feature:Dashboard", "projectNumber": 68, "columnName": "Inbox"}, {"label": "Feature:Drilldowns", "projectNumber": 68, "columnName": "Inbox"}, diff --git a/.i18nrc.json b/.i18nrc.json index 390e5e917d08e..732644b43e1f7 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -16,6 +16,7 @@ "esUi": "src/plugins/es_ui_shared", "devTools": "src/plugins/dev_tools", "expressions": "src/plugins/expressions", + "expressionError": "src/plugins/expression_error", "expressionRevealImage": "src/plugins/expression_reveal_image", "inputControl": "src/plugins/input_control_vis", "inspector": "src/plugins/inspector", diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 2144fd171ff7a..c1535e8a2146f 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -72,6 +72,10 @@ This API doesn't support angular, for registering angular dev tools, bootstrap a |This plugin contains reusable code in the form of self-contained modules (or libraries). Each of these modules exports a set of functionality relevant to the domain of the module. +|{kib-repo}blob/{branch}/src/plugins/expression_error/README.md[expressionError] +|Expression Error plugin adds an error renderer to the expression plugin. The renderer will display the error image. + + |{kib-repo}blob/{branch}/src/plugins/expression_reveal_image/README.md[expressionRevealImage] |Expression Reveal Image plugin adds a revealImage function to the expression plugin and an associated renderer. The renderer will display the given percentage of a given image. @@ -354,10 +358,6 @@ The plugin exposes the static DefaultEditorController class to consume. The client-side plugin configures following values: -|{kib-repo}blob/{branch}/x-pack/plugins/console_extensions/README.md[consoleExtensions] -|This plugin provides autocomplete definitions of licensed APIs to the OSS Console plugin. - - |{kib-repo}blob/{branch}/x-pack/plugins/cross_cluster_replication/README.md[crossClusterReplication] |You can run a local cluster and simulate a remote cluster within a single Kibana directory. diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatterntype.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatterntype.md new file mode 100644 index 0000000000000..46fd3a0725e40 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatterntype.md @@ -0,0 +1,19 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternType](./kibana-plugin-plugins-data-public.indexpatterntype.md) + +## IndexPatternType enum + +Signature: + +```typescript +export declare enum IndexPatternType +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| DEFAULT | "default" | | +| ROLLUP | "rollup" | | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatterntypemeta.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatterntypemeta.md index e6690b244c9ea..19a884862d460 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatterntypemeta.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatterntypemeta.md @@ -15,4 +15,5 @@ export interface TypeMeta | Property | Type | Description | | --- | --- | --- | | [aggs](./kibana-plugin-plugins-data-public.indexpatterntypemeta.aggs.md) | Record<string, AggregationRestrictions> | | +| [params](./kibana-plugin-plugins-data-public.indexpatterntypemeta.params.md) | {
rollup_index: string;
} | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatterntypemeta.params.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatterntypemeta.params.md new file mode 100644 index 0000000000000..12646a39188a0 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatterntypemeta.params.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternTypeMeta](./kibana-plugin-plugins-data-public.indexpatterntypemeta.md) > [params](./kibana-plugin-plugins-data-public.indexpatterntypemeta.params.md) + +## IndexPatternTypeMeta.params property + +Signature: + +```typescript +params?: { + rollup_index: string; + }; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md index 65c4601d5faec..7c2911875ee05 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md @@ -31,6 +31,7 @@ | --- | --- | | [BUCKET\_TYPES](./kibana-plugin-plugins-data-public.bucket_types.md) | | | [ES\_FIELD\_TYPES](./kibana-plugin-plugins-data-public.es_field_types.md) | \* | +| [IndexPatternType](./kibana-plugin-plugins-data-public.indexpatterntype.md) | | | [KBN\_FIELD\_TYPES](./kibana-plugin-plugins-data-public.kbn_field_types.md) | \* | | [METRIC\_TYPES](./kibana-plugin-plugins-data-public.metric_types.md) | | | [QuerySuggestionTypes](./kibana-plugin-plugins-data-public.querysuggestiontypes.md) | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querysuggestiongetfnargs.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querysuggestiongetfnargs.md index de6f4563b678a..7c850a89dff13 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querysuggestiongetfnargs.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querysuggestiongetfnargs.md @@ -19,6 +19,7 @@ export interface QuerySuggestionGetFnArgs | [boolFilter](./kibana-plugin-plugins-data-public.querysuggestiongetfnargs.boolfilter.md) | any | | | [indexPatterns](./kibana-plugin-plugins-data-public.querysuggestiongetfnargs.indexpatterns.md) | IIndexPattern[] | | | [language](./kibana-plugin-plugins-data-public.querysuggestiongetfnargs.language.md) | string | | +| [method](./kibana-plugin-plugins-data-public.querysuggestiongetfnargs.method.md) | ValueSuggestionsMethod | | | [query](./kibana-plugin-plugins-data-public.querysuggestiongetfnargs.query.md) | string | | | [selectionEnd](./kibana-plugin-plugins-data-public.querysuggestiongetfnargs.selectionend.md) | number | | | [selectionStart](./kibana-plugin-plugins-data-public.querysuggestiongetfnargs.selectionstart.md) | number | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querysuggestiongetfnargs.method.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querysuggestiongetfnargs.method.md new file mode 100644 index 0000000000000..2bc9a4fba61c3 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querysuggestiongetfnargs.method.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QuerySuggestionGetFnArgs](./kibana-plugin-plugins-data-public.querysuggestiongetfnargs.md) > [method](./kibana-plugin-plugins-data-public.querysuggestiongetfnargs.method.md) + +## QuerySuggestionGetFnArgs.method property + +Signature: + +```typescript +method?: ValueSuggestionsMethod; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.md index edaf1c9a9ce9e..040bed5a8ce53 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.md @@ -39,5 +39,5 @@ export declare class ExecutionN.B. input is initialized to null rather than undefined for legacy reasons, because in legacy interpreter it was set to null by default. | +| [start(input, isSubExpression)](./kibana-plugin-plugins-expressions-public.execution.start.md) | | Call this method to start execution.N.B. input is initialized to null rather than undefined for legacy reasons, because in legacy interpreter it was set to null by default. | diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.start.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.start.md index 352226da6d72a..b1fa6d7d518b9 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.start.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.start.md @@ -11,7 +11,7 @@ N.B. `input` is initialized to `null` rather than `undefined` for legacy reasons Signature: ```typescript -start(input?: Input): Observable>; +start(input?: Input, isSubExpression?: boolean): Observable>; ``` ## Parameters @@ -19,6 +19,7 @@ start(input?: Input): Observable> | Parameter | Type | Description | | --- | --- | --- | | input | Input | | +| isSubExpression | boolean | | Returns: diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.md index 47963e5e5ef46..44d16ea02e270 100644 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.md +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.md @@ -39,5 +39,5 @@ export declare class ExecutionN.B. input is initialized to null rather than undefined for legacy reasons, because in legacy interpreter it was set to null by default. | +| [start(input, isSubExpression)](./kibana-plugin-plugins-expressions-server.execution.start.md) | | Call this method to start execution.N.B. input is initialized to null rather than undefined for legacy reasons, because in legacy interpreter it was set to null by default. | diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.start.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.start.md index 0eef7013cb3c6..23b4d414d09d1 100644 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.start.md +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.start.md @@ -11,7 +11,7 @@ N.B. `input` is initialized to `null` rather than `undefined` for legacy reasons Signature: ```typescript -start(input?: Input): Observable>; +start(input?: Input, isSubExpression?: boolean): Observable>; ``` ## Parameters @@ -19,6 +19,7 @@ start(input?: Input): Observable> | Parameter | Type | Description | | --- | --- | --- | | input | Input | | +| isSubExpression | boolean | | Returns: diff --git a/docs/getting-started/images/add-sample-data.png b/docs/getting-started/images/add-sample-data.png index 9dee27dcde71b..07a536b19d7d0 100644 Binary files a/docs/getting-started/images/add-sample-data.png and b/docs/getting-started/images/add-sample-data.png differ diff --git a/docs/getting-started/images/tutorial-discover-3.png b/docs/getting-started/images/tutorial-discover-3.png index b024ad6dc39fe..79cf94058bb76 100644 Binary files a/docs/getting-started/images/tutorial-discover-3.png and b/docs/getting-started/images/tutorial-discover-3.png differ diff --git a/docs/getting-started/images/tutorial-discover-4.png b/docs/getting-started/images/tutorial-discover-4.png index 945a6155c02cd..584221e8cfd04 100644 Binary files a/docs/getting-started/images/tutorial-discover-4.png and b/docs/getting-started/images/tutorial-discover-4.png differ diff --git a/docs/getting-started/images/tutorial-final-dashboard.gif b/docs/getting-started/images/tutorial-final-dashboard.gif index 53b7bc04c5f65..6c82c4e53ca10 100644 Binary files a/docs/getting-started/images/tutorial-final-dashboard.gif and b/docs/getting-started/images/tutorial-final-dashboard.gif differ diff --git a/docs/getting-started/images/tutorial-sample-dashboard.png b/docs/getting-started/images/tutorial-sample-dashboard.png index 4c95c04c5e43e..5e06009d0824e 100644 Binary files a/docs/getting-started/images/tutorial-sample-dashboard.png and b/docs/getting-started/images/tutorial-sample-dashboard.png differ diff --git a/docs/getting-started/images/tutorial-sample-filter.png b/docs/getting-started/images/tutorial-sample-filter.png index 56ebacadbef45..8823da311ebb5 100644 Binary files a/docs/getting-started/images/tutorial-sample-filter.png and b/docs/getting-started/images/tutorial-sample-filter.png differ diff --git a/docs/getting-started/images/tutorial-sample-filter2.png b/docs/getting-started/images/tutorial-sample-filter2.png index 21402feacdecd..4215b63d89fa1 100644 Binary files a/docs/getting-started/images/tutorial-sample-filter2.png and b/docs/getting-started/images/tutorial-sample-filter2.png differ diff --git a/docs/getting-started/images/tutorial-sample-query.png b/docs/getting-started/images/tutorial-sample-query.png deleted file mode 100644 index 4f1ca24924b28..0000000000000 Binary files a/docs/getting-started/images/tutorial-sample-query.png and /dev/null differ diff --git a/docs/getting-started/images/tutorial-sample-query2.png b/docs/getting-started/images/tutorial-sample-query2.png deleted file mode 100644 index 0e91e1069a201..0000000000000 Binary files a/docs/getting-started/images/tutorial-sample-query2.png and /dev/null differ diff --git a/docs/getting-started/images/tutorial-treemap.png b/docs/getting-started/images/tutorial-treemap.png deleted file mode 100644 index 32e14fd2308e3..0000000000000 Binary files a/docs/getting-started/images/tutorial-treemap.png and /dev/null differ diff --git a/docs/getting-started/images/tutorial-visualization-dropdown.png b/docs/getting-started/images/tutorial-visualization-dropdown.png index 29d1b99700964..a069af95ed14a 100644 Binary files a/docs/getting-started/images/tutorial-visualization-dropdown.png and b/docs/getting-started/images/tutorial-visualization-dropdown.png differ diff --git a/docs/getting-started/images/tutorial-visualization-treemap.png b/docs/getting-started/images/tutorial-visualization-treemap.png new file mode 100644 index 0000000000000..c6e8db133cb44 Binary files /dev/null and b/docs/getting-started/images/tutorial-visualization-treemap.png differ diff --git a/docs/getting-started/quick-start-guide.asciidoc b/docs/getting-started/quick-start-guide.asciidoc index d9835b312f3ee..ed249008ac8de 100644 --- a/docs/getting-started/quick-start-guide.asciidoc +++ b/docs/getting-started/quick-start-guide.asciidoc @@ -7,7 +7,7 @@ When you've finished, you'll know how to: * <> -* <> +* <> [float] === Required privileges @@ -24,125 +24,125 @@ include::{docs-root}/shared/cloud/ess-getting-started.asciidoc[] [[gs-get-data-into-kibana]] == Add the sample data -Sample data sets come with sample visualizations, dashboards, and more to help you explore {kib} without adding your own data. +Sample data sets come with sample visualizations, dashboards, and more to help you explore {kib} before you ingest or add your own data. -. From the home page, click *Try our sample data*. +. On the home page, click *Try our sample data*. . On the *Sample eCommerce orders* card, click *Add data*. + [role="screenshot"] -image::getting-started/images/add-sample-data.png[Add data UI] +image::images/add-sample-data.png[Add data UI for the sample data sets] [float] [[explore-the-data]] == Explore the data -*Discover* displays an interactive histogram that shows the distribution of of data, or documents, over time, and a table that lists the fields for each document that matches the index. By default, all fields are shown for each matching document. +*Discover* displays the data in an interactive histogram that shows the distribution of data, or documents, over time, and a table that lists the fields for each document that matches the index pattern. To view a subset of the documents, you can apply filters to the data, and customize the table to display only the fields you want to explore. . Open the main menu, then click *Discover*. . Change the <> to *Last 7 days*. + [role="screenshot"] -image::images/tutorial-discover-2.png[] +image::images/tutorial-discover-2.png[Time filter menu with Last 7 days filter configured] -. To focus in on the documents you want to view, use the <>. In the *KQL* search field, enter: +. To view the sales orders for women's clothing that are $60 or more, use the <> search field: + [source,text] -products.taxless_price >= 60 AND category : Women's Clothing -+ -The query returns the women's clothing orders for $60 and more. +products.taxless_price >= 60 and category : Women's Clothing + [role="screenshot"] -image::images/tutorial-discover-4.png[] +image::images/tutorial-discover-4.png[Discover tables that displays only the orders for women's clothing that are $60 or more] -. Hover over the list of *Available fields*, then click *+* next to the fields you want to view in the table. -+ -For example, when you add the *category* field, the table displays the product categories for the orders. +. To view only the product categories that contain sales orders, hover over the *category* field, then click *+*. + [role="screenshot"] -image::images/tutorial-discover-3.png[] -+ -For more information, refer to <>. +image::images/tutorial-discover-3.png[Discover table that displays only the product categories that contain orders] [float] [[view-and-analyze-the-data]] == View and analyze the data -A dashboard is a collection of panels that you can use to view and analyze the data. Panels contain visualizations, interactive controls, Markdown, and more. +A dashboard is a collection of panels that you can use to view and analyze the data. Panels contain visualizations, interactive controls, text, and more. . Open the main menu, then click *Dashboard*. . Click *[eCommerce] Revenue Dashboard*. + [role="screenshot"] -image::getting-started/images/tutorial-sample-dashboard.png[] +image::getting-started/images/tutorial-sample-dashboard.png[The [eCommerce] Revenue Dashboard that comes with the Sample eCommerce order data set] [float] -[[filter-and-query-the-data]] -=== Filter the data +[[create-a-visualization]] +=== Create a visualization panel + +Create a treemap panel that shows the top sales regions and manufacturers, then add the panel to the dashboard. -To focus in on the data you want to view on the dashboard, use filters. +. From the toolbar, click *Edit*, then click *Create visualzation*. -. From the *[eCommerce] Controls* panel, make a selection from the *Manufacturer* and *Category* dropdowns, then click *Apply changes*. +. Open the *Chart type* menu, then select *Treemap*. + -For example, the following dashboard shows the data for women's clothing from Gnomehouse. +[role="screenshot"] +image::getting-started/images/tutorial-visualization-dropdown.png[Chart type menu with Treemap selected] + +. From the *Available fields* list, drag and drop the following fields onto the workspace: + +* *geoip.city_name* + +* *manufacturer.keyword* + [role="screenshot"] -image::getting-started/images/tutorial-sample-filter.png[] +image::getting-started/images/tutorial-visualization-treemap.png[Treemap that displays Top values of geoip.city_name and Top values or manufacturer.keyword fields] -. To manually add a filter, click *Add filter*, then specify the options. +. Click *Save and return*. + -For example, to view the orders for Wednesday, select *day_of_week* from the *Field* dropdown, select *is* from the *Operator* dropdown, then select *Wednesday* from the *Value* dropdown. +The treemap appears as the last visualization panel on the dashboard. + [role="screenshot"] -image::getting-started/images/tutorial-sample-filter2.png[] +image::getting-started/images/tutorial-final-dashboard.gif[Final dashboard with new treemap visualization] + +[float] +[[interact-with-the-data]] +=== Interact with the data + +You can interact with the dashboard data using controls that allow you to apply dashboard-level filters. Interact with the *[eCommerce] Controls* panel to view the women's clothing data from the Gnomehouse manufacturer. -. When you are done, remove the filters. +. From the *Manufacturer* dropdown, select *Gnomehouse*. + +. From the *Category* dropdown, select *Women's Clothing*. + +. Click *Apply changes*. + -For more information, refer to <>. +[role="screenshot"] +image::getting-started/images/tutorial-sample-filter.png[The [eCommerce] Revenue Dashboard that shows only the women's clothing data from the Gnomehouse manufacturer] [float] -[[create-a-visualization]] -=== Create a visualization panel - -Create a treemap panel that shows the top regions and manufacturers, then add the panel to the dashboard. +[[filter-and-query-the-data]] +=== Filter the data -. From the toolbar, click *Edit*, then click *Create new*. +To view a subset of the data, you can apply filters to the dashboard panels. Apply a filter to view the women's clothing data generated on Wednesday from the Gnomehouse manufacturer. -. On the *New Visualization* window, click *Lens*. +. Click *Add filter*. -. From the *Available fields* list, drag and drop the following fields to the visualization builder: +. From the *Field* dropdown, select *day_of_week*. -* *geoip.city_name* +. From the *Operator* dropdown, select *is*. -* *manufacturer.keyword* -+ -. From the visualization dropdown, select *Treemap*. -+ -[role="screenshot"] -image::getting-started/images/tutorial-visualization-dropdown.png[Visualization dropdown with Treemap selected] +. From the *Value* dropdown, select *Wednesday*. . Click *Save*. - -. On the *Save Lens visualization*, enter a title and make sure *Add to Dashboard after saving* is selected, then click *Save and return*. -+ -The treemap appears as the last visualization panel on the dashboard. + [role="screenshot"] -image::getting-started/images/tutorial-final-dashboard.gif[Final dashboard with new treemap visualization] -+ -For more information, refer to <>. +image::getting-started/images/tutorial-sample-filter2.png[The [eCommerce] Revenue Dashboard that shows only the women's clothing data generated on Wednesday from the Gnomehouse manufacturer] [float] [[quick-start-whats-next]] == What's next? -If you are you ready to add your own data, refer to <>. +*Add your own data.* Ready to add your own data? Go to {fleet-guide}/fleet-quick-start.html[Quick start: Get logs and metrics into the Elastic Stack] to learn how to ingest your data, or go to <> and learn about all the other ways you can add data. -If you want to ingest your data, refer to {fleet-guide}/fleet-quick-start.html[Quick start: Get logs and metrics into the Elastic Stack]. +*Explore your own data in Discover.* Ready to learn more about exploring your data in *Discover*? Go to <>. -If you want to secure access to your data, refer to our guide on <> +*Create a dashboard with your own data.* Ready to learn more about analyzing your data in *Dashboard*? Go to <>. -If you want to try out {ml-features} with the sample data sets, refer to -{ml-docs}/ml-getting-started.html[Getting started with {ml}]. \ No newline at end of file +*Try out the {ml-features}.* Ready to analyze the sample data sets and generate models for its patterns of behavior? Go to {ml-docs}/ml-getting-started.html[Getting started with {ml}]. \ No newline at end of file diff --git a/docs/management/images/management-create-rollup-bar-chart.png b/docs/management/images/management-create-rollup-bar-chart.png index 324cfcb9ee5fb..68ba4344c0ecf 100644 Binary files a/docs/management/images/management-create-rollup-bar-chart.png and b/docs/management/images/management-create-rollup-bar-chart.png differ diff --git a/docs/management/images/management-rollup-index-pattern.png b/docs/management/images/management-rollup-index-pattern.png index 57ac00be7977c..de7976e63f050 100644 Binary files a/docs/management/images/management-rollup-index-pattern.png and b/docs/management/images/management-rollup-index-pattern.png differ diff --git a/docs/management/images/management_create_rollup_job.png b/docs/management/images/management_create_rollup_job.png index c3139c9f8df1a..f1dd1580c3a86 100644 Binary files a/docs/management/images/management_create_rollup_job.png and b/docs/management/images/management_create_rollup_job.png differ diff --git a/docs/management/images/management_rollup_job_dashboard.png b/docs/management/images/management_rollup_job_dashboard.png index d3c394183cad8..9573ab7a863e8 100644 Binary files a/docs/management/images/management_rollup_job_dashboard.png and b/docs/management/images/management_rollup_job_dashboard.png differ diff --git a/docs/management/images/management_rollup_job_details.png b/docs/management/images/management_rollup_job_details.png index e6e93a6dae130..5372ba4ad7d13 100644 Binary files a/docs/management/images/management_rollup_job_details.png and b/docs/management/images/management_rollup_job_details.png differ diff --git a/docs/management/images/management_rollup_job_vis.png b/docs/management/images/management_rollup_job_vis.png deleted file mode 100644 index e1f46e4db5c0a..0000000000000 Binary files a/docs/management/images/management_rollup_job_vis.png and /dev/null differ diff --git a/docs/management/images/management_rollup_list.png b/docs/management/images/management_rollup_list.png index 60e9a35071003..505930bcb9a38 100644 Binary files a/docs/management/images/management_rollup_list.png and b/docs/management/images/management_rollup_list.png differ diff --git a/docs/management/rollups/create_and_manage_rollups.asciidoc b/docs/management/rollups/create_and_manage_rollups.asciidoc index bde2ca472b258..51821a935d3f5 100644 --- a/docs/management/rollups/create_and_manage_rollups.asciidoc +++ b/docs/management/rollups/create_and_manage_rollups.asciidoc @@ -64,13 +64,16 @@ You can read more at {ref}/rollup-job-config.html[rollup job configuration]. === Try it: Create and visualize rolled up data This example creates a rollup job to capture log data from sample web logs. -To follow along, add the sample web logs data set. +Before you start, <>. In this example, you want data that is older than 7 days in the target index pattern `kibana_sample_data_logs` -to roll up once a day into the index `rollup_logstash`. You’ll bucket the +to roll up into the `rollup_logstash` index. You’ll bucket the rolled up data on an hourly basis, using 60m for the time bucket configuration. This allows for more granular queries, such as 2h and 12h. +For this example, the job will perform the rollup every minute. However, you'd +typically roll up less frequently in production. + [float] ==== Create the rollup job @@ -80,7 +83,7 @@ As you walk through the *Create rollup job* UI, enter the data: |*Field* |*Value* |Name -|logs_job +|`logs_job` |Index pattern |`kibana_sample_data_logs` @@ -89,12 +92,13 @@ As you walk through the *Create rollup job* UI, enter the data: |`rollup_logstash` |Frequency -|Every day at midnight +|Every minute |Page size |1000 -|Delay (latency buffer)|7d +|Latency buffer +|7d |Date field |@timestamp @@ -118,6 +122,8 @@ As you walk through the *Create rollup job* UI, enter the data: |bytes (average) |=== +On the **Review and save** page, click **Start job now** and **Save**. + The terms, histogram, and metrics fields reflect the key information to retain in the rolled up data: where visitors are from (geo.src), what operating system they are using (machine.os.keyword), @@ -133,7 +139,6 @@ rollup index, or you can remove or archive it using < Index Patterns*. . Click *Create index pattern*, and select *Rollup index pattern* from the dropdown. @@ -149,7 +154,11 @@ is `rollup_logstash,kibana_sample_data_logs`. In this index pattern, `rollup_log matches the rolled up index pattern and `kibana_sample_data_logs` matches the index pattern for raw data. -. Open the main menu, click *Dashboard*, then create and add a vertical bar chart. +. Open the main menu, click *Dashboard*, then *Create dashboard*. + +. Set the <> to *Last 90 days*. + +. On the dashboard, click *Create visualization*. . Choose `rollup_logstash,kibana_sample_data_logs` as your source to see both the raw and rolled up data. @@ -157,13 +166,15 @@ as your source to see both the raw and rolled up data. [role="screenshot"] image::images/management-create-rollup-bar-chart.png[][Create visualization of rolled up data] -. Look at the data in your visualization. -+ -[role="screenshot"] -image::images/management_rollup_job_vis.png[][Visualization of rolled up data] +. Select *Bar vertical stacked* in the chart type dropdown. -. Optionally, create a dashboard that contains visualizations of the rolled up -data, raw data, or both. +. Add the `@timestamp` field to the *Horizontal axis*. + +. Add the `bytes` field to the *Vertical axis*, defaulting to an `Average of +bytes`. ++ +{kib} creates a vertical bar chart of your data. Select a section of the chart +to zoom in. + [role="screenshot"] image::images/management_rollup_job_dashboard.png[][Dashboard with rolled up data] diff --git a/docs/management/snapshot-restore/images/create-policy-example.png b/docs/management/snapshot-restore/images/create-policy-example.png old mode 100755 new mode 100644 index e871c925f5fd5..4ab5e438b306b Binary files a/docs/management/snapshot-restore/images/create-policy-example.png and b/docs/management/snapshot-restore/images/create-policy-example.png differ diff --git a/docs/management/snapshot-restore/images/create-policy.png b/docs/management/snapshot-restore/images/create-policy.png old mode 100755 new mode 100644 index d9a0dce0f4190..3ba33e2522bd5 Binary files a/docs/management/snapshot-restore/images/create-policy.png and b/docs/management/snapshot-restore/images/create-policy.png differ diff --git a/docs/management/snapshot-restore/images/create_snapshot.png b/docs/management/snapshot-restore/images/create_snapshot.png deleted file mode 100644 index 14c1229a23ce1..0000000000000 Binary files a/docs/management/snapshot-restore/images/create_snapshot.png and /dev/null differ diff --git a/docs/management/snapshot-restore/images/register_repo.png b/docs/management/snapshot-restore/images/register_repo.png old mode 100755 new mode 100644 index 9e7ee9db4ce91..c742028ce108c Binary files a/docs/management/snapshot-restore/images/register_repo.png and b/docs/management/snapshot-restore/images/register_repo.png differ diff --git a/docs/management/snapshot-restore/images/repository_list.png b/docs/management/snapshot-restore/images/repository_list.png old mode 100755 new mode 100644 index a4678e87bfb2c..c4eb4fc1a3d1a Binary files a/docs/management/snapshot-restore/images/repository_list.png and b/docs/management/snapshot-restore/images/repository_list.png differ diff --git a/docs/management/snapshot-restore/images/restore-status.png b/docs/management/snapshot-restore/images/restore-status.png deleted file mode 100755 index fa48e32d2fef3..0000000000000 Binary files a/docs/management/snapshot-restore/images/restore-status.png and /dev/null differ diff --git a/docs/management/snapshot-restore/images/snapshot-restore.png b/docs/management/snapshot-restore/images/snapshot-restore.png old mode 100755 new mode 100644 index 41a292f97c853..8ca5dc95e5892 Binary files a/docs/management/snapshot-restore/images/snapshot-restore.png and b/docs/management/snapshot-restore/images/snapshot-restore.png differ diff --git a/docs/management/snapshot-restore/images/snapshot-retention.png b/docs/management/snapshot-restore/images/snapshot-retention.png old mode 100755 new mode 100644 index 7b390357a21b6..44dfecc1a3321 Binary files a/docs/management/snapshot-restore/images/snapshot-retention.png and b/docs/management/snapshot-restore/images/snapshot-retention.png differ diff --git a/docs/management/snapshot-restore/images/snapshot_details.png b/docs/management/snapshot-restore/images/snapshot_details.png old mode 100755 new mode 100644 index 2bd226eecd84e..e6c463d7acb7f Binary files a/docs/management/snapshot-restore/images/snapshot_details.png and b/docs/management/snapshot-restore/images/snapshot_details.png differ diff --git a/docs/management/snapshot-restore/images/snapshot_list.png b/docs/management/snapshot-restore/images/snapshot_list.png old mode 100755 new mode 100644 index dcbb43ec2ab84..f844bfddac4be Binary files a/docs/management/snapshot-restore/images/snapshot_list.png and b/docs/management/snapshot-restore/images/snapshot_list.png differ diff --git a/docs/management/snapshot-restore/images/snapshot_permissions.png b/docs/management/snapshot-restore/images/snapshot_permissions.png deleted file mode 100644 index 463d4d6e389c6..0000000000000 Binary files a/docs/management/snapshot-restore/images/snapshot_permissions.png and /dev/null differ diff --git a/docs/management/snapshot-restore/index.asciidoc b/docs/management/snapshot-restore/index.asciidoc index 62633441ef161..b041bd0873a05 100644 --- a/docs/management/snapshot-restore/index.asciidoc +++ b/docs/management/snapshot-restore/index.asciidoc @@ -2,8 +2,8 @@ [[snapshot-repositories]] == Snapshot and Restore -*Snapshot and Restore* enables you to backup your {es} -indices and clusters using data and state snapshots. +*Snapshot and Restore* lets you back up a running {es} +cluster using data and state snapshots. Snapshots are important because they provide a copy of your data in case something goes wrong. If you need to roll back to an older version of your data, you can restore a snapshot from the repository. @@ -34,17 +34,12 @@ The minimum required permissions to access *Snapshot and Restore* include: To add privileges, open the main menu, then click *Stack Management > Roles*. -[role="screenshot"] -image:management/snapshot-restore/images/snapshot_permissions.png["Edit Role"] - [float] [[kib-snapshot-register-repository]] === Register a repository A repository is where your snapshots live. You must register a snapshot repository before you can perform snapshot and restore operations. -If you don't have a repository, Kibana walks you through the process of -registering one. {kib} supports three repository types out of the box: shared file system, read-only URL, and source-only. For more information on these repositories and their settings, @@ -52,11 +47,9 @@ see {ref}/snapshots-register-repository.html[Repositories]. To use other repositories, such as S3, see {ref}/snapshots-register-repository.html#snapshots-repository-plugins[Repository plugins]. - -Once you create a repository, it is listed in the *Repositories* -view. -Click a repository name to view its type, number of snapshots, and settings, -and to verify status. +The *Repositories* view displays a list of registered repositories. Click a +repository name to view information about the repository, verify its status, or +clean it up. [role="screenshot"] image:management/snapshot-restore/images/repository_list.png["Repository list"] @@ -73,15 +66,8 @@ into each snapshot for further investigation. [role="screenshot"] image:management/snapshot-restore/images/snapshot_details.png["Snapshot details"] -If you don’t have any snapshots, you can create them from the {kib} <>. The -{ref}/snapshots-take-snapshot.html[snapshot API] -takes the current state and data in your index or cluster, and then saves it to a -shared repository. - -The snapshot process is "smart." Your first snapshot is a complete copy of -the data in your index or cluster. -All subsequent snapshots save the changes between the existing snapshots and -the new data. +If you don’t have any snapshots, you can create them using the +{ref}/create-snapshot-api.html[create snapshot API]. [float] [[kib-restore-snapshot]] @@ -93,14 +79,14 @@ restore a snapshot made from one cluster to another cluster. You might use the restore operation to: * Recover data lost due to a failure -* Migrate a current Elasticsearch cluster to a new version +* Migrate an {es} cluster to a new version * Move data from one cluster to another cluster To get started, go to the *Snapshots* view, find the snapshot, and click the restore icon in the *Actions* column. The Restore wizard presents options for the restore operation, including which -indices to restore and whether to modify the index settings. +data streams and indices to restore and whether to change index settings. You can restore an existing index only if it’s closed and has the same number of shards as the index in the snapshot. @@ -119,7 +105,7 @@ Use a {ref}/snapshot-lifecycle-management-api.html[snapshot lifecycle policy] to automate the creation and deletion of cluster snapshots. Taking automatic snapshots: -* Ensures your {es} indices and clusters are backed up on a regular basis +* Ensures your {es} data is backed up on a regular basis * Ensures a recent and relevant snapshot is available if a situation arises where a cluster needs to be recovered * Allows you to manage your snapshots in {kib}, instead of using a @@ -138,8 +124,8 @@ You can drill down into each policy to examine its settings and last successful You can perform the following actions on a snapshot policy: -* *Run* a policy immediately without waiting for the scheduled time. -This action is useful before an upgrade or before performing maintenance on indices. +* *Run* a policy immediately without waiting for the scheduled time. This action +is useful before an upgrade or before performing maintenance. * *Edit* a policy and immediately apply changes to the schedule. * *Delete* a policy to prevent any future snapshots from being taken. This action does not cancel any currently ongoing snapshots or remove any previously taken snapshots. @@ -160,7 +146,7 @@ and then click *Delete snapshots*. [role="xpack"] [[snapshot-restore-tutorial]] -=== Tutorial: Snapshot and Restore +=== Tutorial: Snapshot and Restore Ready to try *Snapshot and Restore*? In this tutorial, you'll learn to: @@ -174,15 +160,12 @@ Ready to try *Snapshot and Restore*? In this tutorial, you'll learn to: This example shows you how to register a shared file system repository and store snapshots. -Before you begin, you must register the location of the repository in the -{ref}/snapshots-register-repository.html#snapshots-filesystem-repository[path.repo] setting on -your master and data nodes. You can do this in one of two ways: -* Edit your `elasticsearch.yml` to include the `path.repo` setting. - -* Pass the `path.repo` setting when you start Elasticsearch. -+ -`bin/elasticsearch -E path.repo=/tmp/es-backups` +Before you begin, you must first mount the file system to the same location on +all master and data nodes. Then add the file system’s path or parent directory +to the +{ref}/snapshots-register-repository.html#snapshots-filesystem-repository[`path.repo`] +setting in `elasticsearch.yml` for each master and data node. [float] [[register-repo-example]] @@ -216,13 +199,10 @@ Use the {ref}/snapshots-take-snapshot.html[snapshot API] to create a snapshot. . Create the snapshot: + [source,js] -PUT /_snapshot/my_backup/2019-04-25_snapshot?wait_for_completion=true +PUT /_snapshot/my_backup/2099-04-25_snapshot?wait_for_completion=true + -In this example, the snapshot name is `2019-04-25_snapshot`. You can also +In this example, the snapshot name is `2099-04-25_snapshot`. You can also use {ref}/date-math-index-names.html[date math expression] for the snapshot name. -+ -[role="screenshot"] -image:management/snapshot-restore/images/create_snapshot.png["Create snapshot"] . Return to *Snapshot and Restore*. + @@ -251,16 +231,17 @@ image:management/snapshot-restore/images/create-policy-example.png["Create polic |Snapshot name |`` -|Schedule -|Every day at 1:30 a.m. - |Repository |`my_backup` +|Schedule +|Every day at 1:30 a.m. + |*Snapshot settings* | -|Indices -|Select the indices to back up. By default, all indices, including system indices, are backed up. +|Data streams and indices +|Select the data streams and indices to back up. By default, all data streams +and indices, including system indices, are backed up. |All other settings |Use the defaults. @@ -280,20 +261,22 @@ Your new policy is listed in the *Policies* view, and you see a summary of its d [[restore-snapshot-example]] ==== Restore a snapshot -Finally, you'll restore indices from an existing snapshot. +Finally, you'll restore data streams and indices from an existing snapshot. -. In the *Snapshots* view, find the snapshot you want to restore, for example `2019-04-25_snapshot`. +. In the *Snapshots* view, find the snapshot you want to restore, for example `2099-04-25_snapshot`. . Click the restore icon in the *Actions* column. . As you walk through the wizard, enter the following values: + |=== |*Logistics* | -|Indices -|Toggle to choose specific indices to restore, or leave in place to restore all indices. +|Data streams and indices +|Toggle to choose specific data streams and indices to restore. Use the default +to restore all data streams and indices in the snapshot. -|Rename indices -|Toggle to give your restored indices new names, or leave in place to restore under original index names. +|Rename data streams and indices +|Toggle to give your restored data streams and indices new names. Use the +default to restore the original data stream and index names. |All other fields |Use the defaults. @@ -313,4 +296,4 @@ or leave in place to keep existing settings. + The operation loads for a few seconds, and then you’re navigated to *Restore Status*, -where you can monitor the status of your restored indices. +where you can monitor the status of your restored data streams and indices. diff --git a/docs/maps/images/gs_dashboard_with_map.png b/docs/maps/images/gs_dashboard_with_map.png index 49b71c16c12b2..a4bf95948edf0 100644 Binary files a/docs/maps/images/gs_dashboard_with_map.png and b/docs/maps/images/gs_dashboard_with_map.png differ diff --git a/docs/maps/images/gs_dashboard_with_terms_filter.png b/docs/maps/images/gs_dashboard_with_terms_filter.png index 21b5c044cb35d..bf84b2ee371af 100644 Binary files a/docs/maps/images/gs_dashboard_with_terms_filter.png and b/docs/maps/images/gs_dashboard_with_terms_filter.png differ diff --git a/docs/maps/images/layer_search.png b/docs/maps/images/layer_search.png index 8e0e8ff628953..d3828ed5f4551 100644 Binary files a/docs/maps/images/layer_search.png and b/docs/maps/images/layer_search.png differ diff --git a/docs/maps/images/quantitative_data_driven_styling.png b/docs/maps/images/quantitative_data_driven_styling.png index a7852ed202016..03dc22f433eee 100644 Binary files a/docs/maps/images/quantitative_data_driven_styling.png and b/docs/maps/images/quantitative_data_driven_styling.png differ diff --git a/docs/maps/images/sample_data_ecommerce.png b/docs/maps/images/sample_data_ecommerce.png index 5b261bb535022..7fba3da608d15 100644 Binary files a/docs/maps/images/sample_data_ecommerce.png and b/docs/maps/images/sample_data_ecommerce.png differ diff --git a/docs/maps/images/sample_data_web_logs.png b/docs/maps/images/sample_data_web_logs.png index f4f4de88f1992..e4902c3e89610 100644 Binary files a/docs/maps/images/sample_data_web_logs.png and b/docs/maps/images/sample_data_web_logs.png differ diff --git a/docs/maps/images/vector_style_class.png b/docs/maps/images/vector_style_class.png index 8c685dfcf0ab6..69549b9f5f2d8 100644 Binary files a/docs/maps/images/vector_style_class.png and b/docs/maps/images/vector_style_class.png differ diff --git a/docs/maps/images/vector_style_dynamic.png b/docs/maps/images/vector_style_dynamic.png index aeaef412b5220..3032e74180afa 100644 Binary files a/docs/maps/images/vector_style_dynamic.png and b/docs/maps/images/vector_style_dynamic.png differ diff --git a/docs/maps/images/vector_style_static.png b/docs/maps/images/vector_style_static.png index 47d9c3b21fcb6..34908aa02fac7 100644 Binary files a/docs/maps/images/vector_style_static.png and b/docs/maps/images/vector_style_static.png differ diff --git a/docs/maps/vector-layer.asciidoc b/docs/maps/vector-layer.asciidoc index 2115c16a889c6..5017ecf91dffd 100644 --- a/docs/maps/vector-layer.asciidoc +++ b/docs/maps/vector-layer.asciidoc @@ -20,10 +20,10 @@ The index must contain at least one field mapped as {ref}/geo-point.html[geo_poi Results are limited to the `index.max_result_window` index setting, which defaults to 10000. Select the appropriate *Scaling* option for your use case. + -* *Limit results to 10000.* The layer displays features from the first `index.max_result_window` documents. +* *Limit results to 10,000* The layer displays features from the first `index.max_result_window` documents. Results exceeding `index.max_result_window` are not displayed. -* *Show clusters when results exceed 10000.* When results exceed `index.max_result_window`, the layer uses {ref}/search-aggregations-bucket-geotilegrid-aggregation.html[GeoTile grid aggregation] to group your documents into clusters and displays metrics for each cluster. When results are less then `index.max_result_window`, the layer displays features from individual documents. +* *Show clusters when results exceed 10,000* When results exceed `index.max_result_window`, the layer uses {ref}/search-aggregations-bucket-geotilegrid-aggregation.html[GeoTile grid aggregation] to group your documents into clusters and displays metrics for each cluster. When results are less then `index.max_result_window`, the layer displays features from individual documents. * *Use vector tiles.* Vector tiles partition your map into 6 to 8 tiles. Each tile request is limited to the `index.max_result_window` index setting. diff --git a/docs/user/management.asciidoc b/docs/user/management.asciidoc index b86fa82c30381..2f9f1fe371dc3 100644 --- a/docs/user/management.asciidoc +++ b/docs/user/management.asciidoc @@ -82,9 +82,10 @@ connectors>> for triggering actions. | Monitor the generation of reports—PDF, PNG, and CSV—and download reports that you previously generated. A report can contain a dashboard, visualization, saved search, or Canvas workpad. -| {ml-docs}/ml-jobs.html[Machine Learning Jobs] -| View your {anomaly-jobs} and {dfanalytics-jobs}. Open the Single Metric -Viewer or Anomaly Explorer to see your {ml} results. +| Machine Learning Jobs +| View your <> and +<> jobs. Open the Single Metric +Viewer or Anomaly Explorer to see your {anomaly-detect} results. | <> | Detect changes in your data by creating, managing, and monitoring alerts. diff --git a/docs/user/ml/index.asciidoc b/docs/user/ml/index.asciidoc index b3606b122d750..a05ff1eeec4a6 100644 --- a/docs/user/ml/index.asciidoc +++ b/docs/user/ml/index.asciidoc @@ -48,8 +48,9 @@ pane: image::user/ml/images/ml-job-management.png[Job Management] You can use the *Settings* pane to create and edit -{ml-docs}/ml-calendars.html[calendars] and the filters that are used in -{ml-docs}/ml-rules.html[custom rules]: +{ml-docs}/ml-ad-finding-anomalies.html#ml-ad-calendars[calendars] and the +filters that are used in +{ml-docs}/ml-ad-finding-anomalies.html#ml-ad-rules[custom rules]: [role="screenshot"] image::user/ml/images/ml-settings.png[Calendar Management] diff --git a/docs/user/production-considerations/task-manager-health-monitoring.asciidoc b/docs/user/production-considerations/task-manager-health-monitoring.asciidoc index 8f2c8d106c77c..3321a9d0c02a1 100644 --- a/docs/user/production-considerations/task-manager-health-monitoring.asciidoc +++ b/docs/user/production-considerations/task-manager-health-monitoring.asciidoc @@ -57,8 +57,12 @@ xpack.task_manager.monitored_task_execution_thresholds: The health API is best consumed by via the `/api/task_manager/_health` endpoint. -Additionally, the metrics are logged in the {kib} `DEBUG` logger at a regular cadence. -To enable Task Manager DEBUG logging in your {kib} instance, add the following to your `kibana.yml`: +Additionally, there are two ways to consume these metrics: + +*Debug logging* + +The metrics are logged in the {kib} `DEBUG` logger at a regular cadence. +To enable Task Manager debug logging in your {kib} instance, add the following to your `kibana.yml`: [source,yml] ---- @@ -69,7 +73,22 @@ logging: level: debug ---- -These stats are logged based the number of milliseconds set in your <> setting, which means it could add substantial noise to your logs. Only enable this level of logging temporarily. +These stats are logged based on the number of milliseconds set in your <> setting, which could add substantial noise to your logs. Only enable this level of logging temporarily. + +*Automatic logging* + +By default, the health API runs at a regular cadence, and each time it runs, it attempts to self evaluate its performance. If this self evaluation yields a potential problem, +a message will log to the {kib} server log. In addition, the health API will look at how long tasks have waited to start (from when they were scheduled to start). If this number exceeds a configurable threshold (<>), the same message as above will log to the {kib} server log. + +This message looks like: + +[source,log] +---- +Detected potential performance issue with Task Manager. Set 'xpack.task_manager.monitored_stats_health_verbose_log.enabled: true' in your Kibana.yml to enable debug logging` +---- + + +If this message appears, set <> to `true` in your `kibana.yml`. This will start logging the health metrics at either a `warn` or `error` log level, depending on the detected severity of the potential problem. [float] [[making-sense-of-task-manager-health-stats]] diff --git a/jest.config.integration.js b/jest.config.integration.js index 8ff142714eebf..e2b2afaa715ee 100644 --- a/jest.config.integration.js +++ b/jest.config.integration.js @@ -6,24 +6,8 @@ * Side Public License, v 1. */ -const preset = require('@kbn/test/jest-preset'); - module.exports = { - preset: '@kbn/test', + preset: '@kbn/test/jest_integration', rootDir: '.', roots: ['/src', '/packages'], - testMatch: ['**/integration_tests**/*.test.{js,mjs,ts,tsx}'], - testPathIgnorePatterns: preset.testPathIgnorePatterns.filter( - (pattern) => !pattern.includes('integration_tests') - ), - setupFilesAfterEnv: [ - '/node_modules/@kbn/test/target_node/jest/setup/after_env.integration.js', - ], - reporters: [ - 'default', - ['@kbn/test/target_node/jest/junit_reporter', { reportName: 'Jest Integration Tests' }], - ], - coverageReporters: !!process.env.CI - ? [['json', { file: 'jest-integration.json' }]] - : ['html', 'text'], }; diff --git a/package.json b/package.json index 5cf72e2110982..2ee46c031fbf0 100644 --- a/package.json +++ b/package.json @@ -83,10 +83,8 @@ "**/minimist": "^1.2.5", "**/node-jose/node-forge": "^0.10.0", "**/pdfkit/crypto-js": "4.0.0", - "**/prismjs": "1.24.0", "**/react-syntax-highlighter": "^15.3.1", "**/react-syntax-highlighter/**/highlight.js": "^10.4.1", - "**/refractor": "^3.3.1", "**/request": "^2.88.2", "**/trim": "1.0.1", "**/typescript": "4.1.3", @@ -103,7 +101,7 @@ "@elastic/datemath": "link:bazel-bin/packages/elastic-datemath", "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@^8.0.0-canary.13", "@elastic/ems-client": "7.14.0", - "@elastic/eui": "34.5.2", + "@elastic/eui": "35.0.0", "@elastic/filesaver": "1.1.2", "@elastic/good": "^9.0.1-kibana3", "@elastic/maki": "6.3.0", @@ -155,6 +153,7 @@ "@kbn/securitysolution-utils": "link:bazel-bin/packages/kbn-securitysolution-utils", "@kbn/server-http-tools": "link:bazel-bin/packages/kbn-server-http-tools", "@kbn/server-route-repository": "link:bazel-bin/packages/kbn-server-route-repository", + "@kbn/typed-react-router-config": "link:bazel-bin/packages/kbn-typed-react-router-config", "@kbn/std": "link:bazel-bin/packages/kbn-std", "@kbn/tinymath": "link:bazel-bin/packages/kbn-tinymath", "@kbn/ui-framework": "link:bazel-bin/packages/kbn-ui-framework", @@ -179,6 +178,7 @@ "@turf/distance": "6.0.1", "@turf/helpers": "6.0.1", "@turf/length": "^6.0.2", + "@types/react-router-config": "^5.0.2", "@types/redux-logger": "^3.0.8", "JSONStream": "1.3.5", "abort-controller": "^3.0.0", @@ -358,6 +358,7 @@ "react-resize-detector": "^4.2.0", "react-reverse-portal": "^1.0.4", "react-router": "^5.2.0", + "react-router-config": "^5.1.1", "react-router-dom": "^5.2.0", "react-router-redux": "^4.0.8", "react-shortcuts": "^2.0.0", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index de7a27fd51276..938afdc205a44 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -56,6 +56,7 @@ filegroup( "//packages/kbn-test:build", "//packages/kbn-test-subj-selector:build", "//packages/kbn-tinymath:build", + "//packages/kbn-typed-react-router-config:build", "//packages/kbn-ui-framework:build", "//packages/kbn-ui-shared-deps:build", "//packages/kbn-utility-types:build", diff --git a/packages/kbn-io-ts-utils/src/deep_exact_rt/index.test.ts b/packages/kbn-io-ts-utils/src/deep_exact_rt/index.test.ts new file mode 100644 index 0000000000000..fe09fb442799c --- /dev/null +++ b/packages/kbn-io-ts-utils/src/deep_exact_rt/index.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import * as t from 'io-ts'; +import { deepExactRt } from '.'; +import { mergeRt } from '../merge_rt'; + +describe('deepExactRt', () => { + it('recursively wraps partial/interface types in t.exact', () => { + const a = t.type({ + path: t.type({ + serviceName: t.string, + }), + query: t.type({ + foo: t.string, + }), + }); + + const b = t.type({ + path: t.type({ + transactionType: t.string, + }), + }); + + const merged = mergeRt(a, b); + + expect( + deepExactRt(a).decode({ + path: { + serviceName: '', + transactionType: '', + }, + query: { + foo: '', + bar: '', + }, + // @ts-ignore + }).right + ).toEqual({ path: { serviceName: '' }, query: { foo: '' } }); + + expect( + deepExactRt(b).decode({ + path: { + serviceName: '', + transactionType: '', + }, + query: { + foo: '', + bar: '', + }, + // @ts-ignore + }).right + ).toEqual({ path: { transactionType: '' } }); + + expect( + deepExactRt(merged).decode({ + path: { + serviceName: '', + transactionType: '', + }, + query: { + foo: '', + bar: '', + }, + // @ts-ignore + }).right + ).toEqual({ path: { serviceName: '', transactionType: '' }, query: { foo: '' } }); + }); +}); diff --git a/packages/kbn-io-ts-utils/src/deep_exact_rt/index.ts b/packages/kbn-io-ts-utils/src/deep_exact_rt/index.ts new file mode 100644 index 0000000000000..8ebb9bbdd52f9 --- /dev/null +++ b/packages/kbn-io-ts-utils/src/deep_exact_rt/index.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import * as t from 'io-ts'; +import { mapValues } from 'lodash'; +import { mergeRt } from '../merge_rt'; +import { isParsableType, ParseableType } from '../parseable_types'; + +export function deepExactRt | ParseableType>(type: T): T; + +export function deepExactRt(type: t.Type | ParseableType) { + if (!isParsableType(type)) { + return type; + } + + switch (type._tag) { + case 'ArrayType': + return t.array(deepExactRt(type.type)); + + case 'DictionaryType': + return t.dictionary(type.domain, deepExactRt(type.codomain)); + + case 'InterfaceType': + return t.exact(t.interface(mapValues(type.props, deepExactRt))); + + case 'PartialType': + return t.exact(t.partial(mapValues(type.props, deepExactRt))); + + case 'IntersectionType': + return t.intersection(type.types.map(deepExactRt) as any); + + case 'UnionType': + return t.union(type.types.map(deepExactRt) as any); + + case 'MergeType': + return mergeRt(deepExactRt(type.types[0]), deepExactRt(type.types[1])); + + default: + return type; + } +} diff --git a/packages/kbn-io-ts-utils/src/parseable_types/index.ts b/packages/kbn-io-ts-utils/src/parseable_types/index.ts new file mode 100644 index 0000000000000..089717ad8891b --- /dev/null +++ b/packages/kbn-io-ts-utils/src/parseable_types/index.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import * as t from 'io-ts'; +import { MergeType } from '../merge_rt'; + +export type ParseableType = + | t.StringType + | t.NumberType + | t.BooleanType + | t.ArrayType + | t.RecordC + | t.DictionaryType + | t.InterfaceType + | t.PartialType + | t.UnionType + | t.IntersectionType + | MergeType; + +const parseableTags = [ + 'StringType', + 'NumberType', + 'BooleanType', + 'ArrayType', + 'DictionaryType', + 'InterfaceType', + 'PartialType', + 'UnionType', + 'IntersectionType', + 'MergeType', +]; + +export const isParsableType = (type: t.Type | ParseableType): type is ParseableType => { + return '_tag' in type && parseableTags.includes(type._tag); +}; diff --git a/packages/kbn-io-ts-utils/src/to_json_schema/index.ts b/packages/kbn-io-ts-utils/src/to_json_schema/index.ts index fc196a7c3123e..702c0150d07f7 100644 --- a/packages/kbn-io-ts-utils/src/to_json_schema/index.ts +++ b/packages/kbn-io-ts-utils/src/to_json_schema/index.ts @@ -7,35 +7,7 @@ */ import * as t from 'io-ts'; import { mapValues } from 'lodash'; - -type JSONSchemableValueType = - | t.StringType - | t.NumberType - | t.BooleanType - | t.ArrayType - | t.RecordC - | t.DictionaryType - | t.InterfaceType - | t.PartialType - | t.UnionType - | t.IntersectionType; - -const tags = [ - 'StringType', - 'NumberType', - 'BooleanType', - 'ArrayType', - 'DictionaryType', - 'InterfaceType', - 'PartialType', - 'UnionType', - 'IntersectionType', -]; - -const isSchemableValueType = (type: t.Mixed): type is JSONSchemableValueType => { - // @ts-ignore - return tags.includes(type._tag); -}; +import { isParsableType } from '../parseable_types'; interface JSONSchemaObject { type: 'object'; @@ -74,7 +46,7 @@ type JSONSchema = | JSONSchemaAnyOf; export const toJsonSchema = (type: t.Mixed): JSONSchema => { - if (isSchemableValueType(type)) { + if (isParsableType(type)) { switch (type._tag) { case 'ArrayType': return { type: 'array', items: toJsonSchema(type.type) }; diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 4524cbe8912cf..d38c3aa346147 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -112,4 +112,5 @@ pageLoadAssetSize: visTypePie: 35583 expressionRevealImage: 25675 cases: 144442 + expressionError: 22127 userSetup: 18532 diff --git a/packages/kbn-rule-data-utils/package.json b/packages/kbn-rule-data-utils/package.json index 6f0b8439ec891..42223e51ec2d6 100644 --- a/packages/kbn-rule-data-utils/package.json +++ b/packages/kbn-rule-data-utils/package.json @@ -4,10 +4,5 @@ "types": "./target/index.d.ts", "version": "1.0.0", "license": "SSPL-1.0 OR Elastic License 2.0", - "private": true, - "scripts": { - "build": "../../node_modules/.bin/tsc", - "kbn:bootstrap": "yarn build", - "kbn:watch": "yarn build --watch" - } + "private": true } diff --git a/packages/kbn-securitysolution-list-utils/src/autocomplete_operators/index.ts b/packages/kbn-securitysolution-list-utils/src/autocomplete_operators/index.ts index 967cebc360f61..051c359dc4612 100644 --- a/packages/kbn-securitysolution-list-utils/src/autocomplete_operators/index.ts +++ b/packages/kbn-securitysolution-list-utils/src/autocomplete_operators/index.ts @@ -11,8 +11,7 @@ import { ListOperatorEnum as OperatorEnum, ListOperatorTypeEnum as OperatorTypeEnum, } from '@kbn/securitysolution-io-ts-list-types'; - -import { OperatorOption } from './types'; +import { OperatorOption } from '../types'; export const isOperator: OperatorOption = { message: i18n.translate('lists.exceptions.isOperatorLabel', { diff --git a/packages/kbn-securitysolution-list-utils/src/helpers/index.ts b/packages/kbn-securitysolution-list-utils/src/helpers/index.ts index d208624b69fc5..38446b2a08ec0 100644 --- a/packages/kbn-securitysolution-list-utils/src/helpers/index.ts +++ b/packages/kbn-securitysolution-list-utils/src/helpers/index.ts @@ -43,7 +43,6 @@ import { isOneOfOperator, isOperator, } from '../autocomplete_operators'; -import { OperatorOption } from '../autocomplete_operators/types'; import { BuilderEntry, @@ -52,6 +51,7 @@ import { EmptyNestedEntry, ExceptionsBuilderExceptionItem, FormattedBuilderEntry, + OperatorOption, } from '../types'; export const isEntryNested = (item: BuilderEntry): item is EntryNested => { diff --git a/packages/kbn-securitysolution-list-utils/src/types/index.ts b/packages/kbn-securitysolution-list-utils/src/types/index.ts index faf68ca157981..537ac06a49f34 100644 --- a/packages/kbn-securitysolution-list-utils/src/types/index.ts +++ b/packages/kbn-securitysolution-list-utils/src/types/index.ts @@ -23,7 +23,12 @@ import { EXCEPTION_LIST_NAMESPACE_AGNOSTIC, } from '@kbn/securitysolution-list-constants'; -import type { OperatorOption } from '../autocomplete_operators/types'; +export interface OperatorOption { + message: string; + value: string; + operator: OperatorEnum; + type: OperatorTypeEnum; +} /** * @deprecated Use the one from core once it is in its own package which will be from: diff --git a/packages/kbn-spec-to-console/README.md b/packages/kbn-spec-to-console/README.md index 0328dec791320..a0e654713f61b 100644 --- a/packages/kbn-spec-to-console/README.md +++ b/packages/kbn-spec-to-console/README.md @@ -18,15 +18,10 @@ git pull --depth=1 origin master ### Usage -You need to run the command twice: once for the **OSS** specs and once for the **X-Pack** specs At the root of the Kibana repository, run the following commands: ```sh -# OSS yarn spec_to_console -g "/rest-api-spec/src/main/resources/rest-api-spec/api/*" -d "src/plugins/console/server/lib/spec_definitions/json/generated" - -# X-pack -yarn spec_to_console -g "/x-pack/plugin/src/test/resources/rest-api-spec/api/*" -d "x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated" ``` ### Information used in Console that is not available in the REST spec diff --git a/packages/kbn-spec-to-console/lib/convert/params.js b/packages/kbn-spec-to-console/lib/convert/params.js index e5365b4d7311e..1aa89be11c76d 100644 --- a/packages/kbn-spec-to-console/lib/convert/params.js +++ b/packages/kbn-spec-to-console/lib/convert/params.js @@ -37,6 +37,7 @@ module.exports = (params) => { case 'string': case 'number': case 'number|string': + case 'boolean|long': result[param] = defaultValue || ''; break; case 'list': diff --git a/packages/kbn-test/BUILD.bazel b/packages/kbn-test/BUILD.bazel index 28c42a4c47684..432778f888e7a 100644 --- a/packages/kbn-test/BUILD.bazel +++ b/packages/kbn-test/BUILD.bazel @@ -28,6 +28,7 @@ filegroup( NPM_MODULE_EXTRA_FILES = [ "jest/package.json", "jest-preset.js", + "jest_integration/jest-preset.js", "jest.config.js", "README.md", "package.json", diff --git a/packages/kbn-test/jest_integration/jest-preset.js b/packages/kbn-test/jest_integration/jest-preset.js new file mode 100644 index 0000000000000..7504dec9e7a20 --- /dev/null +++ b/packages/kbn-test/jest_integration/jest-preset.js @@ -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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +const preset = require('../jest-preset'); + +module.exports = { + ...preset, + testMatch: ['**/integration_tests**/*.test.{js,mjs,ts,tsx}'], + testPathIgnorePatterns: preset.testPathIgnorePatterns.filter( + (pattern) => !pattern.includes('integration_tests') + ), + setupFilesAfterEnv: [ + '/node_modules/@kbn/test/target_node/jest/setup/after_env.integration.js', + '/node_modules/@kbn/test/target_node/jest/setup/mocks.js', + ], + reporters: [ + 'default', + ['@kbn/test/target_node/jest/junit_reporter', { reportName: 'Jest Integration Tests' }], + ], + coverageReporters: !!process.env.CI + ? [['json', { file: 'jest-integration.json' }]] + : ['html', 'text'], +}; diff --git a/packages/kbn-typed-react-router-config/BUILD.bazel b/packages/kbn-typed-react-router-config/BUILD.bazel new file mode 100644 index 0000000000000..90f1acf43d3e7 --- /dev/null +++ b/packages/kbn-typed-react-router-config/BUILD.bazel @@ -0,0 +1,113 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") + +PKG_BASE_NAME = "kbn-typed-react-router-config" +PKG_REQUIRE_NAME = "@kbn/typed-react-router-config" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + "src/**/*.tsx", + ], + exclude = [ + "**/*.test.*", + ] +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +SRC_DEPS = [ + "@npm//tslib", + "@npm//utility-types", + "@npm//io-ts", + "@npm//query-string", + "@npm//react-router-config", + "@npm//react-router-dom", + "//packages/kbn-io-ts-utils", +] + +TYPES_DEPS = [ + "@npm//@types/jest", + "@npm//@types/node", + "@npm//@types/react-router-config", + "@npm//@types/react-router-dom", +] + +DEPS = SRC_DEPS + TYPES_DEPS + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + ], +) + +ts_config( + name = "tsconfig_browser", + src = "tsconfig.browser.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.browser.json", + ], +) + +ts_project( + name = "tsc", + args = ['--pretty'], + srcs = SRCS, + deps = DEPS, + declaration = True, + declaration_dir = "target_types", + declaration_map = True, + incremental = True, + out_dir = "target_node", + source_map = True, + root_dir = "src", + tsconfig = ":tsconfig", +) + +ts_project( + name = "tsc_browser", + args = ['--pretty'], + srcs = SRCS, + deps = DEPS, + declaration = False, + incremental = True, + out_dir = "target_web", + source_map = True, + root_dir = "src", + tsconfig = ":tsconfig_browser", +) + +js_library( + name = PKG_BASE_NAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = DEPS + [":tsc", ":tsc_browser"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [ + ":%s" % PKG_BASE_NAME, + ] +) + +filegroup( + name = "build", + srcs = [ + ":npm_module", + ], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-typed-react-router-config/jest.config.js b/packages/kbn-typed-react-router-config/jest.config.js new file mode 100644 index 0000000000000..3a6b09c5677db --- /dev/null +++ b/packages/kbn-typed-react-router-config/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-typed-react-router-config'], +}; diff --git a/packages/kbn-typed-react-router-config/package.json b/packages/kbn-typed-react-router-config/package.json new file mode 100644 index 0000000000000..50c2e4b5d7e89 --- /dev/null +++ b/packages/kbn-typed-react-router-config/package.json @@ -0,0 +1,9 @@ +{ + "name": "@kbn/typed-react-router-config", + "main": "target_node/index.js", + "types": "target_types/index.d.ts", + "browser": "target_web/index.js", + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0", + "private": true +} diff --git a/packages/kbn-typed-react-router-config/src/create_router.test.tsx b/packages/kbn-typed-react-router-config/src/create_router.test.tsx new file mode 100644 index 0000000000000..49f6961fa3a85 --- /dev/null +++ b/packages/kbn-typed-react-router-config/src/create_router.test.tsx @@ -0,0 +1,239 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import React from 'react'; +import * as t from 'io-ts'; +import { toNumberRt } from '@kbn/io-ts-utils/target/to_number_rt'; +import { createRouter } from './create_router'; +import { createMemoryHistory } from 'history'; +import { route } from './route'; + +describe('createRouter', () => { + const routes = route([ + { + path: '/', + element: <>, + children: [ + { + path: '/', + element: <>, + params: t.type({ + query: t.type({ + rangeFrom: t.string, + rangeTo: t.string, + }), + }), + children: [ + { + path: '/services', + element: <>, + params: t.type({ + query: t.type({ + transactionType: t.string, + }), + }), + }, + { + path: '/services/:serviceName', + element: <>, + params: t.type({ + path: t.type({ + serviceName: t.string, + }), + query: t.type({ + transactionType: t.string, + environment: t.string, + }), + }), + }, + { + path: '/traces', + element: <>, + params: t.type({ + query: t.type({ + aggregationType: t.string, + }), + }), + }, + { + path: '/service-map', + element: <>, + params: t.type({ + query: t.type({ + maxNumNodes: t.string.pipe(toNumberRt as any), + }), + }), + }, + ], + }, + ], + }, + ] as const); + + let history = createMemoryHistory(); + const router = createRouter(routes); + + beforeEach(() => { + history = createMemoryHistory(); + }); + + describe('getParams', () => { + it('returns parameters for routes matching the path only', () => { + history.push('/services?rangeFrom=now-15m&rangeTo=now&transactionType=request'); + const topLevelParams = router.getParams('/', history.location); + + expect(topLevelParams).toEqual({ + path: {}, + query: { + rangeFrom: 'now-15m', + rangeTo: 'now', + }, + }); + + history.push('/services?rangeFrom=now-15m&rangeTo=now&transactionType=request'); + + const inventoryParams = router.getParams('/services', history.location); + + expect(inventoryParams).toEqual({ + path: {}, + query: { + rangeFrom: 'now-15m', + rangeTo: 'now', + transactionType: 'request', + }, + }); + + history.push('/traces?rangeFrom=now-15m&rangeTo=now&aggregationType=avg'); + + const topTracesParams = router.getParams('/traces', history.location); + + expect(topTracesParams).toEqual({ + path: {}, + query: { + rangeFrom: 'now-15m', + rangeTo: 'now', + aggregationType: 'avg', + }, + }); + + history.push( + '/services/opbeans-java?rangeFrom=now-15m&rangeTo=now&environment=production&transactionType=request' + ); + + const serviceOverviewParams = router.getParams('/services/:serviceName', history.location); + + expect(serviceOverviewParams).toEqual({ + path: { + serviceName: 'opbeans-java', + }, + query: { + rangeFrom: 'now-15m', + rangeTo: 'now', + environment: 'production', + transactionType: 'request', + }, + }); + }); + + it('decodes the path and query parameters based on the route type', () => { + history.push('/service-map?rangeFrom=now-15m&rangeTo=now&maxNumNodes=3'); + const topServiceMapParams = router.getParams('/service-map', history.location); + + expect(topServiceMapParams).toEqual({ + path: {}, + query: { + rangeFrom: 'now-15m', + rangeTo: 'now', + maxNumNodes: 3, + }, + }); + }); + + it('throws an error if the given path does not match any routes', () => { + expect(() => { + router.getParams('/service-map', history.location); + }).toThrowError('No matching route found for /service-map'); + }); + }); + + describe('matchRoutes', () => { + it('returns only the routes matching the path', () => { + history.push('/service-map?rangeFrom=now-15m&rangeTo=now&maxNumNodes=3'); + + expect(router.matchRoutes('/', history.location).length).toEqual(2); + expect(router.matchRoutes('/service-map', history.location).length).toEqual(3); + }); + + it('throws an error if the given path does not match any routes', () => { + history.push('/service-map?rangeFrom=now-15m&rangeTo=now&maxNumNodes=3'); + + expect(() => { + router.matchRoutes('/traces', history.location); + }).toThrowError('No matching route found for /traces'); + }); + }); + + describe('link', () => { + it('returns a link for the given route', () => { + const serviceOverviewLink = router.link('/services/:serviceName', { + path: { serviceName: 'opbeans-java' }, + query: { + rangeFrom: 'now-15m', + rangeTo: 'now', + environment: 'production', + transactionType: 'request', + }, + }); + + expect(serviceOverviewLink).toEqual( + '/services/opbeans-java?environment=production&rangeFrom=now-15m&rangeTo=now&transactionType=request' + ); + + const servicesLink = router.link('/services', { + query: { + rangeFrom: 'now-15m', + rangeTo: 'now', + transactionType: 'request', + }, + }); + + expect(servicesLink).toEqual( + '/services?rangeFrom=now-15m&rangeTo=now&transactionType=request' + ); + + const serviceMapLink = router.link('/service-map', { + query: { + maxNumNodes: '3', + rangeFrom: 'now-15m', + rangeTo: 'now', + }, + }); + + expect(serviceMapLink).toEqual('/service-map?maxNumNodes=3&rangeFrom=now-15m&rangeTo=now'); + }); + + it('validates the parameters needed for the route', () => { + expect(() => { + router.link('/traces', { + query: { + rangeFrom: {}, + }, + } as any); + }).toThrowError(); + + expect(() => { + router.link('/service-map', { + query: { + maxNumNodes: 3, + rangeFrom: 'now-15m', + rangeTo: 'now', + }, + } as any); + }).toThrowError(); + }); + }); +}); diff --git a/packages/kbn-typed-react-router-config/src/create_router.ts b/packages/kbn-typed-react-router-config/src/create_router.ts new file mode 100644 index 0000000000000..51b6e2a2f5692 --- /dev/null +++ b/packages/kbn-typed-react-router-config/src/create_router.ts @@ -0,0 +1,158 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { isLeft } from 'fp-ts/lib/Either'; +import { Location } from 'history'; +import { PathReporter } from 'io-ts/lib/PathReporter'; +import { + matchRoutes as matchRoutesConfig, + RouteConfig as ReactRouterConfig, +} from 'react-router-config'; +import qs from 'query-string'; +import { findLastIndex, merge, compact } from 'lodash'; +import { deepExactRt } from '@kbn/io-ts-utils/target/deep_exact_rt'; +import { mergeRt } from '@kbn/io-ts-utils/target/merge_rt'; +import { Route, Router } from './types'; + +export function createRouter(routes: TRoutes): Router { + const routesByReactRouterConfig = new Map(); + const reactRouterConfigsByRoute = new Map(); + + const reactRouterConfigs = routes.map((route) => toReactRouterConfigRoute(route)); + + function toReactRouterConfigRoute(route: Route, prefix: string = ''): ReactRouterConfig { + const path = `${prefix}${route.path}`.replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; + const reactRouterConfig: ReactRouterConfig = { + component: () => route.element, + routes: + (route.children as Route[] | undefined)?.map((child) => + toReactRouterConfigRoute(child, path) + ) ?? [], + exact: !route.children?.length, + path, + }; + + routesByReactRouterConfig.set(reactRouterConfig, route); + reactRouterConfigsByRoute.set(route, reactRouterConfig); + + return reactRouterConfig; + } + + const matchRoutes = (...args: any[]) => { + let path: string = args[0]; + let location: Location = args[1]; + + if (args.length === 1) { + location = args[0] as Location; + path = location.pathname; + } + + const greedy = path.endsWith('/*') || args.length === 1; + + if (!path) { + path = '/'; + } + + const matches = matchRoutesConfig(reactRouterConfigs, location.pathname); + + const matchIndex = greedy + ? matches.length - 1 + : findLastIndex(matches, (match) => match.route.path === path); + + if (matchIndex === -1) { + throw new Error(`No matching route found for ${path}`); + } + + return matches.slice(0, matchIndex + 1).map((matchedRoute) => { + const route = routesByReactRouterConfig.get(matchedRoute.route); + + if (route?.params) { + const decoded = deepExactRt(route.params).decode({ + path: matchedRoute.match.params, + query: qs.parse(location.search), + }); + + if (isLeft(decoded)) { + throw new Error(PathReporter.report(decoded).join('\n')); + } + + return { + match: { + ...matchedRoute.match, + params: decoded.right, + }, + route, + }; + } + + return { + match: { + ...matchedRoute.match, + params: { + path: {}, + query: {}, + }, + }, + route, + }; + }); + }; + + const link = (path: string, ...args: any[]) => { + const params: { path?: Record; query?: Record } | undefined = args[0]; + + const paramsWithDefaults = merge({ path: {}, query: {} }, params); + + path = path + .split('/') + .map((part) => { + return part.startsWith(':') ? paramsWithDefaults.path[part.split(':')[1]] : part; + }) + .join('/'); + + const matches = matchRoutesConfig(reactRouterConfigs, path); + + if (!matches.length) { + throw new Error(`No matching route found for ${path}`); + } + + const validationType = mergeRt( + ...(compact( + matches.map((match) => { + return routesByReactRouterConfig.get(match.route)?.params; + }) + ) as [any, any]) + ); + + const validation = validationType.decode(paramsWithDefaults); + + if (isLeft(validation)) { + throw new Error(PathReporter.report(validation).join('\n')); + } + + return qs.stringifyUrl({ + url: path, + query: paramsWithDefaults.query, + }); + }; + + return { + link: (path, ...args) => { + return link(path, ...args); + }, + getParams: (path, location) => { + const matches = matchRoutes(path, location); + return merge({ path: {}, query: {} }, ...matches.map((match) => match.match.params)); + }, + matchRoutes: (...args: any[]) => { + return matchRoutes(...args) as any; + }, + getRoutePath: (route) => { + return reactRouterConfigsByRoute.get(route)!.path as string; + }, + }; +} diff --git a/packages/kbn-typed-react-router-config/src/index.ts b/packages/kbn-typed-react-router-config/src/index.ts new file mode 100644 index 0000000000000..b58c70998901c --- /dev/null +++ b/packages/kbn-typed-react-router-config/src/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +export * from './create_router'; +export * from './types'; +export * from './outlet'; +export * from './route'; +export * from './route_renderer'; +export * from './router_provider'; +export * from './unconst'; +export * from './use_current_route'; +export * from './use_match_routes'; +export * from './use_params'; +export * from './use_router'; +export * from './use_route_path'; diff --git a/packages/kbn-typed-react-router-config/src/outlet.tsx b/packages/kbn-typed-react-router-config/src/outlet.tsx new file mode 100644 index 0000000000000..696085489abee --- /dev/null +++ b/packages/kbn-typed-react-router-config/src/outlet.tsx @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { useCurrentRoute } from './use_current_route'; + +export function Outlet() { + const { element } = useCurrentRoute(); + return element; +} diff --git a/packages/kbn-typed-react-router-config/src/route.ts b/packages/kbn-typed-react-router-config/src/route.ts new file mode 100644 index 0000000000000..b9b228d1009e2 --- /dev/null +++ b/packages/kbn-typed-react-router-config/src/route.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { Route } from './types'; +import { Unconst, unconst } from './unconst'; + +export function route( + r: TRoute +): Unconst { + return unconst(r); +} diff --git a/packages/kbn-typed-react-router-config/src/route_renderer.tsx b/packages/kbn-typed-react-router-config/src/route_renderer.tsx new file mode 100644 index 0000000000000..e7a39aa7d5d16 --- /dev/null +++ b/packages/kbn-typed-react-router-config/src/route_renderer.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import React from 'react'; +import { CurrentRouteContextProvider } from './use_current_route'; +import { RouteMatch } from './types'; +import { useMatchRoutes } from './use_match_routes'; + +export function RouteRenderer() { + const matches: RouteMatch[] = useMatchRoutes(); + + return matches + .concat() + .reverse() + .reduce((prev, match) => { + const { element } = match.route; + return ( + + {element} + + ); + }, <>); +} diff --git a/packages/kbn-typed-react-router-config/src/router_provider.tsx b/packages/kbn-typed-react-router-config/src/router_provider.tsx new file mode 100644 index 0000000000000..d2512ba8fe426 --- /dev/null +++ b/packages/kbn-typed-react-router-config/src/router_provider.tsx @@ -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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { History } from 'history'; +import React from 'react'; +import { Router as ReactRouter } from 'react-router-dom'; +import { Route, Router } from './types'; +import { RouterContextProvider } from './use_router'; + +export function RouterProvider({ + children, + router, + history, +}: { + router: Router; + history: History; + children: React.ReactElement; +}) { + return ( + + {children} + + ); +} diff --git a/packages/kbn-typed-react-router-config/src/types/index.ts b/packages/kbn-typed-react-router-config/src/types/index.ts new file mode 100644 index 0000000000000..dfd9893966491 --- /dev/null +++ b/packages/kbn-typed-react-router-config/src/types/index.ts @@ -0,0 +1,421 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Location } from 'history'; +import * as t from 'io-ts'; +import { ReactElement } from 'react'; +import { RequiredKeys, ValuesType } from 'utility-types'; +// import { unconst } from '../unconst'; +import { NormalizePath } from './utils'; + +export type PathsOf = keyof MapRoutes & string; + +export interface RouteMatch { + route: TRoute; + match: { + isExact: boolean; + path: string; + url: string; + params: TRoute extends { + params: t.Type; + } + ? t.OutputOf + : {}; + }; +} + +type ToRouteMatch = TRoutes extends [] + ? [] + : TRoutes extends [Route] + ? [RouteMatch] + : TRoutes extends [Route, ...infer TTail] + ? TTail extends Route[] + ? [RouteMatch, ...ToRouteMatch] + : [] + : []; + +type UnwrapRouteMap = TRoute extends { + parents: Route[]; +} + ? ToRouteMatch<[...TRoute['parents'], Omit]> + : ToRouteMatch<[Omit]>; + +export type Match = MapRoutes extends { + [key in TPath]: Route; +} + ? UnwrapRouteMap[TPath]> + : []; + +interface PlainRoute { + path: string; + element: ReactElement; + children?: PlainRoute[]; + params?: t.Type; +} + +interface ReadonlyPlainRoute { + readonly path: string; + readonly element: ReactElement; + readonly children?: readonly ReadonlyPlainRoute[]; + readonly params?: t.Type; +} + +export type Route = PlainRoute | ReadonlyPlainRoute; + +interface DefaultOutput { + path: {}; + query: {}; +} + +type OutputOfRouteMatch = TRouteMatch extends { + route: { params: t.Type }; +} + ? t.OutputOf + : DefaultOutput; + +type OutputOfMatches = TRouteMatches extends [RouteMatch] + ? OutputOfRouteMatch + : TRouteMatches extends [RouteMatch, ...infer TNextRouteMatches] + ? OutputOfRouteMatch & + (TNextRouteMatches extends RouteMatch[] ? OutputOfMatches : DefaultOutput) + : TRouteMatches extends RouteMatch[] + ? OutputOfRouteMatch> + : DefaultOutput; + +export type OutputOf> = OutputOfMatches< + Match +> & + DefaultOutput; + +type TypeOfRouteMatch = TRouteMatch extends { + route: { params: t.Type }; +} + ? t.TypeOf + : {}; + +type TypeOfMatches = TRouteMatches extends [RouteMatch] + ? TypeOfRouteMatch + : TRouteMatches extends [RouteMatch, ...infer TNextRouteMatches] + ? TypeOfRouteMatch & + (TNextRouteMatches extends RouteMatch[] ? TypeOfMatches : {}) + : {}; + +export type TypeOf> = TypeOfMatches< + Match +>; + +export type TypeAsArgs = keyof TObject extends never + ? [] + : RequiredKeys extends never + ? [TObject] | [] + : [TObject]; + +export interface Router { + matchRoutes>( + path: TPath, + location: Location + ): Match; + matchRoutes(location: Location): Match>; + getParams>( + path: TPath, + location: Location + ): OutputOf; + link>( + path: TPath, + ...args: TypeAsArgs> + ): string; + getRoutePath(route: Route): string; +} + +type AppendPath< + TPrefix extends string, + TPath extends string +> = NormalizePath<`${TPrefix}${NormalizePath<`/${TPath}`>}`>; + +type MaybeUnion, U extends Record> = Omit & + { + [key in keyof U]: key extends keyof T ? T[key] | U[key] : U[key]; + }; + +type MapRoute< + TRoute extends Route, + TPrefix extends string, + TParents extends Route[] = [] +> = TRoute extends Route + ? MaybeUnion< + { + [key in AppendPath]: TRoute & { parents: TParents }; + }, + TRoute extends { children: Route[] } + ? MaybeUnion< + MapRoutes< + TRoute['children'], + AppendPath, + [...TParents, TRoute] + >, + { + [key in AppendPath>]: ValuesType< + MapRoutes< + TRoute['children'], + AppendPath, + [...TParents, TRoute] + > + >; + } + > + : {} + > + : {}; + +type MapRoutes< + TRoutes, + TPrefix extends string = '', + TParents extends Route[] = [] +> = TRoutes extends [Route] + ? MapRoute + : TRoutes extends [Route, Route] + ? MapRoute & MapRoute + : TRoutes extends [Route, Route, Route] + ? MapRoute & + MapRoute & + MapRoute + : TRoutes extends [Route, Route, Route, Route] + ? MapRoute & + MapRoute & + MapRoute & + MapRoute + : TRoutes extends [Route, Route, Route, Route, Route] + ? MapRoute & + MapRoute & + MapRoute & + MapRoute & + MapRoute + : TRoutes extends [Route, Route, Route, Route, Route, Route] + ? MapRoute & + MapRoute & + MapRoute & + MapRoute & + MapRoute & + MapRoute + : TRoutes extends [Route, Route, Route, Route, Route, Route, Route] + ? MapRoute & + MapRoute & + MapRoute & + MapRoute & + MapRoute & + MapRoute & + MapRoute + : TRoutes extends [Route, Route, Route, Route, Route, Route, Route, Route] + ? MapRoute & + MapRoute & + MapRoute & + MapRoute & + MapRoute & + MapRoute & + MapRoute & + MapRoute + : TRoutes extends [Route, Route, Route, Route, Route, Route, Route, Route, Route] + ? MapRoute & + MapRoute & + MapRoute & + MapRoute & + MapRoute & + MapRoute & + MapRoute & + MapRoute & + MapRoute + : TRoutes extends [Route, Route, Route, Route, Route, Route, Route, Route, Route, Route] + ? MapRoute & + MapRoute & + MapRoute & + MapRoute & + MapRoute & + MapRoute & + MapRoute & + MapRoute & + MapRoute & + MapRoute + : {}; + +// const element = null as any; + +// const routes = unconst([ +// { +// path: '/', +// element, +// children: [ +// { +// path: '/settings', +// element, +// children: [ +// { +// path: '/agent-configuration', +// element, +// }, +// { +// path: '/agent-configuration/create', +// element, +// params: t.partial({ +// query: t.partial({ +// pageStep: t.string, +// }), +// }), +// }, +// { +// path: '/agent-configuration/edit', +// element, +// params: t.partial({ +// query: t.partial({ +// pageStep: t.string, +// }), +// }), +// }, +// { +// path: '/apm-indices', +// element, +// }, +// { +// path: '/customize-ui', +// element, +// }, +// { +// path: '/schema', +// element, +// }, +// { +// path: '/anomaly-detection', +// element, +// }, +// { +// path: '/', +// element, +// }, +// ], +// }, +// { +// path: '/services/:serviceName', +// element, +// params: t.intersection([ +// t.type({ +// path: t.type({ +// serviceName: t.string, +// }), +// }), +// t.partial({ +// query: t.partial({ +// environment: t.string, +// rangeFrom: t.string, +// rangeTo: t.string, +// comparisonEnabled: t.string, +// comparisonType: t.string, +// latencyAggregationType: t.string, +// transactionType: t.string, +// kuery: t.string, +// }), +// }), +// ]), +// children: [ +// { +// path: '/overview', +// element, +// }, +// { +// path: '/transactions', +// element, +// }, +// { +// path: '/errors', +// element, +// children: [ +// { +// path: '/:groupId', +// element, +// params: t.type({ +// path: t.type({ +// groupId: t.string, +// }), +// }), +// }, +// { +// path: '/', +// element, +// params: t.partial({ +// query: t.partial({ +// sortDirection: t.string, +// sortField: t.string, +// pageSize: t.string, +// page: t.string, +// }), +// }), +// }, +// ], +// }, +// { +// path: '/foo', +// element, +// }, +// { +// path: '/bar', +// element, +// }, +// { +// path: '/baz', +// element, +// }, +// { +// path: '/', +// element, +// }, +// ], +// }, +// { +// path: '/', +// element, +// params: t.partial({ +// query: t.partial({ +// rangeFrom: t.string, +// rangeTo: t.string, +// }), +// }), +// children: [ +// { +// path: '/services', +// element, +// }, +// { +// path: '/traces', +// element, +// }, +// { +// path: '/service-map', +// element, +// }, +// { +// path: '/', +// element, +// }, +// ], +// }, +// ], +// }, +// ] as const); + +// type Routes = typeof routes; + +// type Mapped = keyof MapRoutes; + +// type Bar = ValuesType>['route']['path']; +// type Foo = OutputOf; + +// const { path }: Foo = {} as any; + +// function _useApmParams>(p: TPath): OutputOf { +// return {} as any; +// } + +// const params = _useApmParams('/*'); diff --git a/packages/kbn-typed-react-router-config/src/types/utils.ts b/packages/kbn-typed-react-router-config/src/types/utils.ts new file mode 100644 index 0000000000000..38b23c5118d0f --- /dev/null +++ b/packages/kbn-typed-react-router-config/src/types/utils.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; + +export type MaybeOutputOf = T extends t.Type ? [t.OutputOf] : []; +export type NormalizePath = T extends `//${infer TRest}` + ? NormalizePath<`/${TRest}`> + : T extends '/' + ? T + : T extends `${infer TRest}/` + ? TRest + : T; +export type DeeplyMutableRoutes = T extends React.ReactElement + ? T + : T extends t.Type + ? T + : T extends readonly [infer U] + ? [DeeplyMutableRoutes] + : T extends readonly [infer U, ...infer V] + ? [DeeplyMutableRoutes, ...DeeplyMutableRoutes] + : T extends Record + ? { + -readonly [key in keyof T]: DeeplyMutableRoutes; + } + : T; diff --git a/packages/kbn-typed-react-router-config/src/unconst.ts b/packages/kbn-typed-react-router-config/src/unconst.ts new file mode 100644 index 0000000000000..d10c8290e20e9 --- /dev/null +++ b/packages/kbn-typed-react-router-config/src/unconst.ts @@ -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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import * as t from 'io-ts'; +import { DeepReadonly } from 'utility-types'; + +export type MaybeConst = TObject extends [object] + ? [DeepReadonly | TObject] + : TObject extends [object, ...infer TTail] + ? [DeepReadonly | TObject, ...(TTail extends object[] ? MaybeConst : [])] + : TObject extends object[] + ? DeepReadonly + : TObject extends object + ? [DeepReadonly | TObject] + : []; + +export type Unconst = T extends React.ReactElement + ? React.ReactElement + : T extends t.Type + ? T + : T extends readonly [any] + ? [Unconst] + : T extends readonly [any, any] + ? [Unconst, Unconst] + : T extends readonly [any, any, any] + ? [Unconst, Unconst, Unconst] + : T extends readonly [any, any, any, any] + ? [Unconst, Unconst, Unconst, Unconst] + : T extends readonly [any, any, any, any, any] + ? [Unconst, Unconst, Unconst, Unconst, Unconst] + : T extends readonly [any, any, any, any, any, any] + ? [Unconst, Unconst, Unconst, Unconst, Unconst, Unconst] + : T extends readonly [any, any, any, any, any, any, any] + ? [ + Unconst, + Unconst, + Unconst, + Unconst, + Unconst, + Unconst, + Unconst + ] + : T extends readonly [any, any, any, any, any, any, any, any] + ? [ + Unconst, + Unconst, + Unconst, + Unconst, + Unconst, + Unconst, + Unconst, + Unconst + ] + : T extends readonly [any, any, any, any, any, any, any, any, any] + ? [ + Unconst, + Unconst, + Unconst, + Unconst, + Unconst, + Unconst, + Unconst, + Unconst, + Unconst + ] + : T extends readonly [any, any, any, any, any, any, any, any, any, any] + ? [ + Unconst, + Unconst, + Unconst, + Unconst, + Unconst, + Unconst, + Unconst, + Unconst, + Unconst, + Unconst + ] + : T extends readonly [infer U, ...infer V] + ? [Unconst, ...Unconst] + : T extends Record + ? { -readonly [key in keyof T]: Unconst } + : T; + +export function unconst(value: T): Unconst { + return value as Unconst; +} diff --git a/packages/kbn-typed-react-router-config/src/use_current_route.tsx b/packages/kbn-typed-react-router-config/src/use_current_route.tsx new file mode 100644 index 0000000000000..9227b119107b3 --- /dev/null +++ b/packages/kbn-typed-react-router-config/src/use_current_route.tsx @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import React, { createContext, useContext } from 'react'; +import { RouteMatch } from './types'; + +const CurrentRouteContext = createContext< + { match: RouteMatch; element: React.ReactElement } | undefined +>(undefined); + +export const CurrentRouteContextProvider = ({ + match, + element, + children, +}: { + match: RouteMatch; + element: React.ReactElement; + children: React.ReactElement; +}) => { + return ( + + {children} + + ); +}; + +export const useCurrentRoute = () => { + const currentRoute = useContext(CurrentRouteContext); + if (!currentRoute) { + throw new Error('No match was found in context'); + } + return currentRoute; +}; diff --git a/packages/kbn-typed-react-router-config/src/use_match_routes.ts b/packages/kbn-typed-react-router-config/src/use_match_routes.ts new file mode 100644 index 0000000000000..b818ff06e9ae6 --- /dev/null +++ b/packages/kbn-typed-react-router-config/src/use_match_routes.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { useLocation } from 'react-router-dom'; +import { RouteMatch } from './types'; +import { useRouter } from './use_router'; + +export function useMatchRoutes(path?: string): RouteMatch[] { + const router = useRouter(); + const location = useLocation(); + + return typeof path === 'undefined' + ? router.matchRoutes(location) + : router.matchRoutes(path as never, location); +} diff --git a/packages/kbn-securitysolution-list-utils/src/autocomplete_operators/types.ts b/packages/kbn-typed-react-router-config/src/use_params.ts similarity index 57% rename from packages/kbn-securitysolution-list-utils/src/autocomplete_operators/types.ts rename to packages/kbn-typed-react-router-config/src/use_params.ts index 1be21bb62a7fe..3f730e5d156f6 100644 --- a/packages/kbn-securitysolution-list-utils/src/autocomplete_operators/types.ts +++ b/packages/kbn-typed-react-router-config/src/use_params.ts @@ -6,14 +6,12 @@ * Side Public License, v 1. */ -import type { - ListOperatorEnum as OperatorEnum, - ListOperatorTypeEnum as OperatorTypeEnum, -} from '@kbn/securitysolution-io-ts-list-types'; +import { useLocation } from 'react-router-dom'; +import { useRouter } from './use_router'; -export interface OperatorOption { - message: string; - value: string; - operator: OperatorEnum; - type: OperatorTypeEnum; +export function useParams(path: string) { + const router = useRouter(); + const location = useLocation(); + + return router.getParams(path as never, location); } diff --git a/packages/kbn-typed-react-router-config/src/use_route_path.tsx b/packages/kbn-typed-react-router-config/src/use_route_path.tsx new file mode 100644 index 0000000000000..c77fc7d04b620 --- /dev/null +++ b/packages/kbn-typed-react-router-config/src/use_route_path.tsx @@ -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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { last } from 'lodash'; +import { useMatchRoutes } from './use_match_routes'; +import { useRouter } from './use_router'; + +export function useRoutePath() { + const lastRouteMatch = last(useMatchRoutes()); + const router = useRouter(); + if (!lastRouteMatch) { + throw new Error('No route was matched'); + } + + return router.getRoutePath(lastRouteMatch.route); +} diff --git a/packages/kbn-typed-react-router-config/src/use_router.tsx b/packages/kbn-typed-react-router-config/src/use_router.tsx new file mode 100644 index 0000000000000..b54530ed0fbdb --- /dev/null +++ b/packages/kbn-typed-react-router-config/src/use_router.tsx @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { createContext, useContext } from 'react'; +import { Route, Router } from './types'; + +const RouterContext = createContext | undefined>(undefined); + +export const RouterContextProvider = ({ + router, + children, +}: { + router: Router; + children: React.ReactElement; +}) => {children}; + +export function useRouter(): Router { + const router = useContext(RouterContext); + + if (!router) { + throw new Error('Router not found in context'); + } + + return router; +} diff --git a/packages/kbn-typed-react-router-config/tsconfig.browser.json b/packages/kbn-typed-react-router-config/tsconfig.browser.json new file mode 100644 index 0000000000000..1de1603fec286 --- /dev/null +++ b/packages/kbn-typed-react-router-config/tsconfig.browser.json @@ -0,0 +1,19 @@ +{ + "extends": "../../tsconfig.browser.json", + "compilerOptions": { + "incremental": true, + "outDir": "./target_web", + "stripInternal": true, + "declaration": false, + "isolatedModules": true, + "sourceMap": true, + "sourceRoot": "../../../../../packages/kbn-typed-react-router-config/src", + "types": [ + "node", + "jest" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/kbn-typed-react-router-config/tsconfig.json b/packages/kbn-typed-react-router-config/tsconfig.json new file mode 100644 index 0000000000000..fb7262aa68662 --- /dev/null +++ b/packages/kbn-typed-react-router-config/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "incremental": true, + "declarationDir": "./target_types", + "outDir": "./target_node", + "stripInternal": true, + "declaration": true, + "declarationMap": true, + "isolatedModules": true, + "sourceMap": true, + "sourceRoot": "../../../../../packages/kbn-typed-react-router-config/src", + "types": [ + "node", + "jest" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index 7a843a41cc4ca..e8453d009e720 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -232,20 +232,20 @@ export class DocLinksService { guide: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/index.html`, aggregations: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-configuring-aggregation.html`, anomalyDetection: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-ad-overview.html`, - anomalyDetectionJobs: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-jobs.html`, + anomalyDetectionJobs: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-ad-finding-anomalies.html`, anomalyDetectionConfiguringCategories: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-configuring-categories.html`, - anomalyDetectionBucketSpan: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/create-jobs.html#bucket-span`, - anomalyDetectionCardinality: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/create-jobs.html#cardinality`, - anomalyDetectionCreateJobs: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/create-jobs.html`, - anomalyDetectionDetectors: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/create-jobs.html#detectors`, - anomalyDetectionInfluencers: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-influencers.html`, + anomalyDetectionBucketSpan: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-ad-finding-anomalies.html#ml-ad-bucket-span`, + anomalyDetectionCardinality: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-ad-finding-anomalies.html#ml-ad-cardinality`, + anomalyDetectionCreateJobs: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-ad-finding-anomalies.html#ml-ad-create-job`, + anomalyDetectionDetectors: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-ad-finding-anomalies.html#ml-ad-detectors`, + anomalyDetectionInfluencers: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-ad-finding-anomalies.html#ml-ad-influencers`, anomalyDetectionJobResource: `${ELASTICSEARCH_DOCS}ml-put-job.html#ml-put-job-path-parms`, anomalyDetectionJobResourceAnalysisConfig: `${ELASTICSEARCH_DOCS}ml-put-job.html#put-analysisconfig`, - anomalyDetectionJobTips: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/create-jobs.html#job-tips`, - anomalyDetectionModelMemoryLimits: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/create-jobs.html#model-memory-limits`, - calendars: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-calendars.html`, + anomalyDetectionJobTips: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-ad-finding-anomalies.html#ml-ad-job-tips`, + anomalyDetectionModelMemoryLimits: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-ad-finding-anomalies.html#ml-ad-model-memory-limits`, + calendars: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-ad-finding-anomalies.html#ml-ad-calendars`, classificationEvaluation: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-dfa-classification.html#ml-dfanalytics-classification-evaluation`, - customRules: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-rules.html`, + customRules: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-ad-finding-anomalies.html#ml-ad-rules`, customUrls: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-configuring-url.html`, dataFrameAnalytics: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-dfanalytics.html`, featureImportance: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-feature-importance.html`, diff --git a/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap b/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap index 801fa452e8332..16504cf97366e 100644 --- a/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap +++ b/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap @@ -29,7 +29,6 @@ exports[`#start() returns \`Context\` component 1`] = ` "euiCodeEditor.stopEditing": "When you're done, press Escape to stop editing.", "euiCodeEditor.stopInteracting": "When you're done, press Escape to stop interacting with the code.", "euiCollapsedItemActions.allActions": "All actions", - "euiCollapsibleNav.closeButtonLabel": "close", "euiColorPicker.alphaLabel": "Alpha channel (opacity) value", "euiColorPicker.closeLabel": "Press the down key to open a popover containing color options", "euiColorPicker.colorErrorMessage": "Invalid color value", @@ -45,6 +44,7 @@ exports[`#start() returns \`Context\` component 1`] = ` "euiColorStopThumb.stopErrorMessage": "Value is out of range", "euiColorStopThumb.stopLabel": "Stop value", "euiColorStops.screenReaderAnnouncement": [Function], + "euiColumnActions.hideColumn": "Hide column", "euiColumnActions.moveLeft": "Move left", "euiColumnActions.moveRight": "Move right", "euiColumnActions.sort": [Function], @@ -75,6 +75,9 @@ exports[`#start() returns \`Context\` component 1`] = ` "euiComboBoxOptionsList.noMatchingOptions": [Function], "euiComboBoxPill.removeSelection": [Function], "euiCommonlyUsedTimeRanges.legend": "Commonly used", + "euiControlBar.customScreenReaderAnnouncement": [Function], + "euiControlBar.screenReaderAnnouncement": "There is a new region landmark with page level controls at the end of the document.", + "euiControlBar.screenReaderHeading": "Page level controls", "euiDataGrid.ariaLabel": [Function], "euiDataGrid.ariaLabelGridPagination": [Function], "euiDataGrid.ariaLabelledBy": [Function], @@ -100,11 +103,11 @@ exports[`#start() returns \`Context\` component 1`] = ` "euiFieldPassword.showPassword": "Show password as plain text. Note: this will visually expose your password on the screen.", "euiFilePicker.clearSelectedFiles": "Clear selected files", "euiFilePicker.filesSelected": "files selected", + "euiFilePicker.removeSelected": "Remove", "euiFilterButton.filterBadge": [Function], "euiFlyout.closeAriaLabel": "Close this dialog", "euiForm.addressFormErrors": "Please address the highlighted errors.", "euiFormControlLayoutClearButton.label": "Clear input", - "euiHeaderAlert.dismiss": "Dismiss", "euiHeaderLinks.appNavigation": "App menu", "euiHeaderLinks.openNavigationMenu": "Open menu", "euiHue.label": "Select the HSV color mode \\"hue\\" value", @@ -134,11 +137,16 @@ exports[`#start() returns \`Context\` component 1`] = ` "euiNotificationEventReadButton.markAsReadAria": [Function], "euiNotificationEventReadButton.markAsUnread": "Mark as unread", "euiNotificationEventReadButton.markAsUnreadAria": [Function], + "euiNotificationEventReadIcon.read": "Read", + "euiNotificationEventReadIcon.readAria": [Function], + "euiNotificationEventReadIcon.unread": "Unread", + "euiNotificationEventReadIcon.unreadAria": [Function], "euiPagination.disabledNextPage": "Next page", "euiPagination.disabledPreviousPage": "Previous page", "euiPagination.firstRangeAriaLabel": [Function], "euiPagination.lastRangeAriaLabel": [Function], "euiPagination.nextPage": [Function], + "euiPagination.pageOfTotalCompressed": [Function], "euiPagination.previousPage": [Function], "euiPaginationButton.longPageString": [Function], "euiPaginationButton.shortPageString": [Function], @@ -211,19 +219,16 @@ exports[`#start() returns \`Context\` component 1`] = ` "euiSuperUpdateButton.refreshButtonLabel": "Refresh", "euiSuperUpdateButton.updateButtonLabel": "Update", "euiSuperUpdateButton.updatingButtonLabel": "Updating", - "euiTableHeaderCell.clickForAscending": "Click to sort in ascending order", - "euiTableHeaderCell.clickForDescending": "Click to sort in descending order", - "euiTableHeaderCell.clickForUnsort": "Click to unsort", - "euiTableHeaderCell.titleTextWithSort": [Function], + "euiTableHeaderCell.titleTextWithDesc": [Function], "euiTablePagination.rowsPerPage": "Rows per page", "euiTablePagination.rowsPerPageOption": [Function], "euiTableSortMobile.sorting": "Sorting", "euiToast.dismissToast": "Dismiss toast", "euiToast.newNotification": "A new notification appears", "euiToast.notification": "Notification", - "euiTour.closeTour": "Close tour", - "euiTour.endTour": "End tour", - "euiTour.skipTour": "Skip tour", + "euiTourStep.closeTour": "Close tour", + "euiTourStep.endTour": "End tour", + "euiTourStep.skipTour": "Skip tour", "euiTourStepIndicator.ariaLabel": [Function], "euiTourStepIndicator.isActive": "active", "euiTourStepIndicator.isComplete": "complete", diff --git a/src/core/public/i18n/i18n_eui_mapping.tsx b/src/core/public/i18n/i18n_eui_mapping.tsx index 1cccc4d94a78d..5e7e6ae7c0e4e 100644 --- a/src/core/public/i18n/i18n_eui_mapping.tsx +++ b/src/core/public/i18n/i18n_eui_mapping.tsx @@ -9,13 +9,14 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiTokensObject } from '@elastic/eui'; interface EuiValues { [key: string]: any; } export const getEuiContextMapping = () => { - const euiContextMapping = { + const euiContextMapping: EuiTokensObject = { 'euiAccordion.isLoading': i18n.translate('core.euiAccordion.isLoading', { defaultMessage: 'Loading', }), @@ -143,12 +144,6 @@ export const getEuiContextMapping = () => { 'ARIA label and tooltip content describing a button that expands an actions menu', } ), - 'euiCollapsibleNav.closeButtonLabel': i18n.translate( - 'core.euiCollapsibleNav.closeButtonLabel', - { - defaultMessage: 'close', - } - ), 'euiColorPicker.screenReaderAnnouncement': i18n.translate( 'core.euiColorPicker.screenReaderAnnouncement', { @@ -235,6 +230,9 @@ export const getEuiContextMapping = () => { 'euiColumnActions.moveRight': i18n.translate('core.euiColumnActions.moveRight', { defaultMessage: 'Move right', }), + 'euiColumnActions.hideColumn': i18n.translate('core.euiColumnActions.hideColumn', { + defaultMessage: 'Hide column', + }), 'euiColumnSelector.hideAll': i18n.translate('core.euiColumnSelector.hideAll', { defaultMessage: 'Hide all', }), @@ -368,6 +366,22 @@ export const getEuiContextMapping = () => { 'euiCommonlyUsedTimeRanges.legend': i18n.translate('core.euiCommonlyUsedTimeRanges.legend', { defaultMessage: 'Commonly used', }), + 'euiControlBar.screenReaderHeading': i18n.translate('core.euiControlBar.screenReaderHeading', { + defaultMessage: 'Page level controls', + }), + 'euiControlBar.customScreenReaderAnnouncement': ({ landmarkHeading }: EuiValues) => + i18n.translate('core.euiControlBar.customScreenReaderAnnouncement', { + defaultMessage: + 'There is a new region landmark called {landmarkHeading} with page level controls at the end of the document.', + values: { landmarkHeading }, + }), + 'euiControlBar.screenReaderAnnouncement': i18n.translate( + 'core.euiControlBar.screenReaderAnnouncement', + { + defaultMessage: + 'There is a new region landmark with page level controls at the end of the document.', + } + ), 'euiDataGrid.screenReaderNotice': i18n.translate('core.euiDataGrid.screenReaderNotice', { defaultMessage: 'Cell contains interactive content.', }), @@ -500,6 +514,9 @@ export const getEuiContextMapping = () => { 'euiFilePicker.filesSelected': i18n.translate('core.euiFilePicker.filesSelected', { defaultMessage: 'files selected', }), + 'euiFilePicker.removeSelected': i18n.translate('core.euiFilePicker.removeSelected', { + defaultMessage: 'Remove', + }), 'euiFilterButton.filterBadge': ({ count, hasActiveFilters }: EuiValues) => i18n.translate('core.euiFilterButton.filterBadge', { defaultMessage: '${count} ${filterCountLabel} filters', @@ -518,10 +535,6 @@ export const getEuiContextMapping = () => { description: 'ARIA label on a button that removes any entry in a form field', } ), - 'euiHeaderAlert.dismiss': i18n.translate('core.euiHeaderAlert.dismiss', { - defaultMessage: 'Dismiss', - description: 'ARIA label on a button that dismisses/removes a notification', - }), 'euiHeaderLinks.appNavigation': i18n.translate('core.euiHeaderLinks.appNavigation', { defaultMessage: 'App menu', description: 'ARIA label on a `nav` element', @@ -669,6 +682,25 @@ export const getEuiContextMapping = () => { defaultMessage: 'Mark as unread', } ), + 'euiNotificationEventReadIcon.readAria': ({ eventName }: EuiValues) => + i18n.translate('core.euiNotificationEventReadIcon.readAria', { + defaultMessage: '{eventName} is read', + values: { eventName }, + }), + 'euiNotificationEventReadIcon.unreadAria': ({ eventName }: EuiValues) => + i18n.translate('core.euiNotificationEventReadIcon.unreadAria', { + defaultMessage: '{eventName} is unread', + values: { eventName }, + }), + 'euiNotificationEventReadIcon.read': i18n.translate('core.euiNotificationEventReadIcon.read', { + defaultMessage: 'Read', + }), + 'euiNotificationEventReadIcon.unread': i18n.translate( + 'core.euiNotificationEventReadIcon.unread', + { + defaultMessage: 'Unread', + } + ), 'euiNotificationEventMessages.accordionHideText': i18n.translate( 'core.euiNotificationEventMessages.accordionHideText', { @@ -680,6 +712,11 @@ export const getEuiContextMapping = () => { defaultMessage: 'Next page, {page}', values: { page }, }), + 'euiPagination.pageOfTotalCompressed': ({ page, total }: EuiValues) => + i18n.translate('core.euiPagination.pageOfTotalCompressed', { + defaultMessage: '{page} of {total}', + values: { page, total }, + }), 'euiPagination.previousPage': ({ page }: EuiValues) => i18n.translate('core.euiPagination.previousPage', { defaultMessage: 'Previous page, {page}', @@ -1043,29 +1080,10 @@ export const getEuiContextMapping = () => { description: 'Displayed in a button that updates based on date picked', } ), - 'euiTableHeaderCell.clickForAscending': i18n.translate( - 'core.euiTableHeaderCell.clickForAscending', - { - defaultMessage: 'Click to sort in ascending order', - description: 'Displayed in a button that toggles a table sorting', - } - ), - 'euiTableHeaderCell.clickForDescending': i18n.translate( - 'core.euiTableHeaderCell.clickForDescending', - { - defaultMessage: 'Click to sort in descending order', - description: 'Displayed in a button that toggles a table sorting', - } - ), - 'euiTableHeaderCell.clickForUnsort': i18n.translate('core.euiTableHeaderCell.clickForUnsort', { - defaultMessage: 'Click to unsort', - description: 'Displayed in a button that toggles a table sorting', - }), - 'euiTableHeaderCell.titleTextWithSort': ({ innerText, ariaSortValue }: EuiValues) => - i18n.translate('core.euiTableHeaderCell.titleTextWithSort', { - defaultMessage: '{innerText}; Sorted in {ariaSortValue} order', - values: { innerText, ariaSortValue }, - description: 'Text describing the table sort order', + 'euiTableHeaderCell.titleTextWithDesc': ({ innerText, description }: EuiValues) => + i18n.translate('core.euiTableHeaderCell.titleTextWithDesc', { + defaultMessage: '{innerText}; {description}', + values: { innerText, description }, }), 'euiTablePagination.rowsPerPage': i18n.translate('core.euiTablePagination.rowsPerPage', { defaultMessage: 'Rows per page', @@ -1091,15 +1109,6 @@ export const getEuiContextMapping = () => { defaultMessage: 'Notification', description: 'ARIA label on an element containing a notification', }), - 'euiTour.endTour': i18n.translate('core.euiTour.endTour', { - defaultMessage: 'End tour', - }), - 'euiTour.skipTour': i18n.translate('core.euiTour.skipTour', { - defaultMessage: 'Skip tour', - }), - 'euiTour.closeTour': i18n.translate('core.euiTour.closeTour', { - defaultMessage: 'Close tour', - }), 'euiTourStepIndicator.isActive': i18n.translate('core.euiTourStepIndicator.isActive', { defaultMessage: 'active', description: 'Text for an active tour step', @@ -1112,6 +1121,15 @@ export const getEuiContextMapping = () => { defaultMessage: 'incomplete', description: 'Text for an incomplete tour step', }), + 'euiTourStep.endTour': i18n.translate('core.euiTourStep.endTour', { + defaultMessage: 'End tour', + }), + 'euiTourStep.skipTour': i18n.translate('core.euiTourStep.skipTour', { + defaultMessage: 'Skip tour', + }), + 'euiTourStep.closeTour': i18n.translate('core.euiTourStep.closeTour', { + defaultMessage: 'Close tour', + }), 'euiTourStepIndicator.ariaLabel': ({ status, number }: EuiValues) => i18n.translate('core.euiTourStepIndicator.ariaLabel', { defaultMessage: 'Step {number} {status}', diff --git a/src/core/server/core_app/bundle_routes/file_hash.test.ts b/src/core/server/core_app/bundle_routes/file_hash.test.ts index 918f435156344..0de63d7409ed9 100644 --- a/src/core/server/core_app/bundle_routes/file_hash.test.ts +++ b/src/core/server/core_app/bundle_routes/file_hash.test.ts @@ -67,6 +67,8 @@ describe('getFileHash', () => { await getFileHash(cache, sampleFilePath, stats, fd); expect(cache.set).toHaveBeenCalledTimes(1); - expect(cache.set).toHaveBeenCalledWith(`${sampleFilePath}-${stats.ino}`, computedHashPromise); + expect(cache.set).toHaveBeenCalledWith(`${sampleFilePath}-${stats.ino}`, expect.any(Promise)); + const promiseValue = await cache.set.mock.calls[0][1]; + expect(promiseValue).toEqual('computed-hash'); }); }); diff --git a/src/core/server/saved_objects/migrations/core/migrate_raw_docs.test.ts b/src/core/server/saved_objects/migrations/core/migrate_raw_docs.test.ts index 0481e6118acb0..c8ff79351aadb 100644 --- a/src/core/server/saved_objects/migrations/core/migrate_raw_docs.test.ts +++ b/src/core/server/saved_objects/migrations/core/migrate_raw_docs.test.ts @@ -137,14 +137,15 @@ describe('migrateRawDocsSafely', () => { const transform = jest.fn((doc: any) => [ set(_.cloneDeep(doc), 'attributes.name', 'HOI!'), ]); - const task = migrateRawDocsSafely( - new SavedObjectsSerializer(new SavedObjectTypeRegistry()), - transform, - [ + const task = migrateRawDocsSafely({ + serializer: new SavedObjectsSerializer(new SavedObjectTypeRegistry()), + knownTypes: new Set(['a', 'c']), + migrateDoc: transform, + rawDocs: [ { _id: 'a:b', _source: { type: 'a', a: { name: 'AAA' } } }, { _id: 'c:d', _source: { type: 'c', c: { name: 'DDD' } } }, - ] - ); + ], + }); const result = (await task()) as Either.Right; expect(result._tag).toEqual('Right'); expect(result.right.processedDocs).toEqual([ @@ -181,14 +182,15 @@ describe('migrateRawDocsSafely', () => { const transform = jest.fn((doc: any) => [ set(_.cloneDeep(doc), 'attributes.name', 'TADA'), ]); - const task = migrateRawDocsSafely( - new SavedObjectsSerializer(new SavedObjectTypeRegistry()), - transform, - [ + const task = migrateRawDocsSafely({ + serializer: new SavedObjectsSerializer(new SavedObjectTypeRegistry()), + knownTypes: new Set(['a', 'c']), + migrateDoc: transform, + rawDocs: [ { _id: 'foo:b', _source: { type: 'a', a: { name: 'AAA' } } }, { _id: 'c:d', _source: { type: 'c', c: { name: 'DDD' } } }, - ] - ); + ], + }); const result = (await task()) as Either.Left; expect(transform).toHaveBeenCalledTimes(1); expect(result._tag).toEqual('Left'); @@ -202,11 +204,12 @@ describe('migrateRawDocsSafely', () => { set(_.cloneDeep(doc), 'attributes.name', 'HOI!'), { id: 'bar', type: 'foo', attributes: { name: 'baz' } }, ]); - const task = migrateRawDocsSafely( - new SavedObjectsSerializer(new SavedObjectTypeRegistry()), - transform, - [{ _id: 'a:b', _source: { type: 'a', a: { name: 'AAA' } } }] - ); + const task = migrateRawDocsSafely({ + serializer: new SavedObjectsSerializer(new SavedObjectTypeRegistry()), + knownTypes: new Set(['a', 'c']), + migrateDoc: transform, + rawDocs: [{ _id: 'a:b', _source: { type: 'a', a: { name: 'AAA' } } }], + }); const result = (await task()) as Either.Right; expect(result._tag).toEqual('Right'); expect(result.right.processedDocs).toEqual([ @@ -235,11 +238,12 @@ describe('migrateRawDocsSafely', () => { const transform = jest.fn((doc: any) => { throw new TransformSavedObjectDocumentError(new Error('error during transform'), '8.0.0'); }); - const task = migrateRawDocsSafely( - new SavedObjectsSerializer(new SavedObjectTypeRegistry()), - transform, - [{ _id: 'a:b', _source: { type: 'a', a: { name: 'AAA' } } }] // this is the raw doc - ); + const task = migrateRawDocsSafely({ + serializer: new SavedObjectsSerializer(new SavedObjectTypeRegistry()), + knownTypes: new Set(['a', 'c']), + migrateDoc: transform, + rawDocs: [{ _id: 'a:b', _source: { type: 'a', a: { name: 'AAA' } } }], // this is the raw doc + }); const result = (await task()) as Either.Left; expect(transform).toHaveBeenCalledTimes(1); expect(result._tag).toEqual('Left'); @@ -252,4 +256,43 @@ describe('migrateRawDocsSafely', () => { } `); }); + + test('skips documents of unknown types', async () => { + const transform = jest.fn((doc: any) => [ + set(_.cloneDeep(doc), 'attributes.name', 'HOI!'), + ]); + const task = migrateRawDocsSafely({ + serializer: new SavedObjectsSerializer(new SavedObjectTypeRegistry()), + knownTypes: new Set(['a']), + migrateDoc: transform, + rawDocs: [ + { _id: 'a:b', _source: { type: 'a', a: { name: 'AAA' } } }, + { _id: 'c:d', _source: { type: 'c', c: { name: 'DDD' } } }, + ], + }); + + const result = (await task()) as Either.Right; + expect(result._tag).toEqual('Right'); + expect(result.right.processedDocs).toEqual([ + { + _id: 'a:b', + _source: { type: 'a', a: { name: 'HOI!' }, migrationVersion: {}, references: [] }, + }, + { + _id: 'c:d', + // name field is not migrated on unknown type + _source: { type: 'c', c: { name: 'DDD' } }, + }, + ]); + + const obj1 = { + id: 'b', + type: 'a', + attributes: { name: 'AAA' }, + migrationVersion: {}, + references: [], + }; + expect(transform).toHaveBeenCalledTimes(1); + expect(transform).toHaveBeenNthCalledWith(1, obj1); + }); }); diff --git a/src/core/server/saved_objects/migrations/core/migrate_raw_docs.ts b/src/core/server/saved_objects/migrations/core/migrate_raw_docs.ts index 461ae1df6bc3d..65ea21a6778d5 100644 --- a/src/core/server/saved_objects/migrations/core/migrate_raw_docs.ts +++ b/src/core/server/saved_objects/migrations/core/migrate_raw_docs.ts @@ -81,6 +81,13 @@ export async function migrateRawDocs( return processedDocs; } +interface MigrateRawDocsSafelyDeps { + serializer: SavedObjectsSerializer; + knownTypes: ReadonlySet; + migrateDoc: MigrateAndConvertFn; + rawDocs: SavedObjectsRawDoc[]; +} + /** * Applies the specified migration function to every saved object document provided * and converts the saved object to a raw document. @@ -88,11 +95,15 @@ export async function migrateRawDocs( * for which the transformation function failed. * @returns {TaskEither.TaskEither} */ -export function migrateRawDocsSafely( - serializer: SavedObjectsSerializer, - migrateDoc: MigrateAndConvertFn, - rawDocs: SavedObjectsRawDoc[] -): TaskEither.TaskEither { +export function migrateRawDocsSafely({ + serializer, + knownTypes, + migrateDoc, + rawDocs, +}: MigrateRawDocsSafelyDeps): TaskEither.TaskEither< + DocumentsTransformFailed, + DocumentsTransformSuccess +> { return async () => { const migrateDocNonBlocking = transformNonBlocking(migrateDoc); const processedDocs: SavedObjectsRawDoc[] = []; @@ -100,7 +111,10 @@ export function migrateRawDocsSafely( const corruptSavedObjectIds: string[] = []; const options = { namespaceTreatment: 'lax' as const }; for (const raw of rawDocs) { - if (serializer.isRawSavedObject(raw, options)) { + // Do not transform documents of unknown types + if (raw?._source?.type && !knownTypes.has(raw._source.type)) { + processedDocs.push(raw); + } else if (serializer.isRawSavedObject(raw, options)) { try { const savedObject = convertToRawAddMigrationVersion(raw, options, serializer); processedDocs.push( diff --git a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts index 2d0282e6d2632..572b2934e49b8 100644 --- a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts +++ b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts @@ -184,11 +184,12 @@ export class KibanaMigrator { logger: this.log, preMigrationScript: indexMap[index].script, transformRawDocs: (rawDocs: SavedObjectsRawDoc[]) => - migrateRawDocsSafely( - this.serializer, - this.documentMigrator.migrateAndConvert, - rawDocs - ), + migrateRawDocsSafely({ + serializer: this.serializer, + knownTypes: new Set(this.typeRegistry.getAllTypes().map((t) => t.name)), + migrateDoc: this.documentMigrator.migrateAndConvert, + rawDocs, + }), migrationVersionPerType: this.documentMigrator.migrationVersion, indexPrefix: index, migrationsConfig: this.soMigrationsConfig, diff --git a/src/core/server/saved_objects/migrationsv2/actions/check_for_unknown_docs.test.ts b/src/core/server/saved_objects/migrationsv2/actions/check_for_unknown_docs.test.ts index 62a619ef447fa..a52cb2a922968 100644 --- a/src/core/server/saved_objects/migrationsv2/actions/check_for_unknown_docs.test.ts +++ b/src/core/server/saved_objects/migrationsv2/actions/check_for_unknown_docs.test.ts @@ -97,9 +97,12 @@ describe('checkForUnknownDocs', () => { const result = await task(); expect(Either.isRight(result)).toBe(true); + expect((result as Either.Right).right).toEqual({ + unknownDocs: [], + }); }); - it('resolves with `Either.left` when unknown docs are found', async () => { + it('resolves with `Either.right` when unknown docs are found', async () => { const client = elasticsearchClientMock.createInternalClient( elasticsearchClientMock.createSuccessTransportRequestPromise({ hits: { @@ -120,9 +123,8 @@ describe('checkForUnknownDocs', () => { const result = await task(); - expect(Either.isLeft(result)).toBe(true); - expect((result as Either.Left).left).toEqual({ - type: 'unknown_docs_found', + expect(Either.isRight(result)).toBe(true); + expect((result as Either.Right).right).toEqual({ unknownDocs: [ { id: '12', type: 'foo' }, { id: '14', type: 'bar' }, @@ -148,9 +150,8 @@ describe('checkForUnknownDocs', () => { const result = await task(); - expect(Either.isLeft(result)).toBe(true); - expect((result as Either.Left).left).toEqual({ - type: 'unknown_docs_found', + expect(Either.isRight(result)).toBe(true); + expect((result as Either.Right).right).toEqual({ unknownDocs: [{ id: '12', type: 'unknown' }], }); }); diff --git a/src/core/server/saved_objects/migrationsv2/actions/check_for_unknown_docs.ts b/src/core/server/saved_objects/migrationsv2/actions/check_for_unknown_docs.ts index 7cc1c26a2ea8b..e3d72fbdf866f 100644 --- a/src/core/server/saved_objects/migrationsv2/actions/check_for_unknown_docs.ts +++ b/src/core/server/saved_objects/migrationsv2/actions/check_for_unknown_docs.ts @@ -32,7 +32,6 @@ export interface CheckForUnknownDocsFoundDoc { /** @internal */ export interface UnknownDocsFound { - type: 'unknown_docs_found'; unknownDocs: CheckForUnknownDocsFoundDoc[]; } @@ -42,8 +41,8 @@ export const checkForUnknownDocs = ({ unusedTypesQuery, knownTypes, }: CheckForUnknownDocsParams): TaskEither.TaskEither< - RetryableEsClientError | UnknownDocsFound, - {} + RetryableEsClientError, + UnknownDocsFound > => () => { const query = createUnknownDocQuery(unusedTypesQuery, knownTypes); @@ -56,14 +55,9 @@ export const checkForUnknownDocs = ({ }) .then((response) => { const { hits } = response.body.hits; - if (hits.length) { - return Either.left({ - type: 'unknown_docs_found' as const, - unknownDocs: hits.map((hit) => ({ id: hit._id, type: hit._source?.type ?? 'unknown' })), - }); - } else { - return Either.right({}); - } + return Either.right({ + unknownDocs: hits.map((hit) => ({ id: hit._id, type: hit._source?.type ?? 'unknown' })), + }); }) .catch(catchRetryableEsClientErrors); }; diff --git a/src/core/server/saved_objects/migrationsv2/actions/index.ts b/src/core/server/saved_objects/migrationsv2/actions/index.ts index 8e4584970f138..6bfcddfe1f6de 100644 --- a/src/core/server/saved_objects/migrationsv2/actions/index.ts +++ b/src/core/server/saved_objects/migrationsv2/actions/index.ts @@ -80,7 +80,6 @@ export type { } from './update_and_pickup_mappings'; export { updateAndPickupMappings } from './update_and_pickup_mappings'; -import type { UnknownDocsFound } from './check_for_unknown_docs'; export type { CheckForUnknownDocsParams, UnknownDocsFound, @@ -131,7 +130,6 @@ export interface ActionErrorTypeMap { alias_not_found_exception: AliasNotFound; remove_index_not_a_concrete_index: RemoveIndexNotAConcreteIndex; documents_transform_failed: DocumentsTransformFailed; - unknown_docs_found: UnknownDocsFound; } /** diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/migration_7_13_0_unknown_types.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/migration_7_13_0_unknown_types.test.ts index c5e302adbe903..a30b3d291e7ec 100644 --- a/src/core/server/saved_objects/migrationsv2/integration_tests/migration_7_13_0_unknown_types.test.ts +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/migration_7_13_0_unknown_types.test.ts @@ -7,42 +7,31 @@ */ import Path from 'path'; -import Fs from 'fs'; -import Util from 'util'; +import fs from 'fs/promises'; import { estypes } from '@elastic/elasticsearch'; import * as kbnTestServer from '../../../../test_helpers/kbn_server'; import { Root } from '../../../root'; +import JSON5 from 'json5'; +import { ElasticsearchClient } from '../../../elasticsearch'; const logFilePath = Path.join(__dirname, '7_13_unknown_types_test.log'); -const asyncUnlink = Util.promisify(Fs.unlink); - async function removeLogFile() { // ignore errors if it doesn't exist - await asyncUnlink(logFilePath).catch(() => void 0); + await fs.unlink(logFilePath).catch(() => void 0); } describe('migration v2', () => { let esServer: kbnTestServer.TestElasticsearchUtils; let root: Root; + let startES: () => Promise; beforeAll(async () => { await removeLogFile(); }); - afterAll(async () => { - if (root) { - await root.shutdown(); - } - if (esServer) { - await esServer.stop(); - } - - await new Promise((resolve) => setTimeout(resolve, 10000)); - }); - - it('migrates the documents to the highest version', async () => { - const { startES } = kbnTestServer.createTestServers({ + beforeEach(() => { + ({ startES } = kbnTestServer.createTestServers({ adjustTimeout: (t: number) => jest.setTimeout(t), settings: { es: { @@ -53,50 +42,155 @@ describe('migration v2', () => { dataArchive: Path.join(__dirname, 'archives', '7.13.0_with_unknown_so.zip'), }, }, - }); + })); + }); + + afterEach(async () => { + if (root) { + await root.shutdown(); + } + if (esServer) { + await esServer.stop(); + } + + await new Promise((resolve) => setTimeout(resolve, 10000)); + }); + it('logs a warning and completes the migration with unknown docs retained', async () => { root = createRoot(); + esServer = await startES(); + await root.setup(); + await root.start(); + + const logFileContent = await fs.readFile(logFilePath, 'utf-8'); + const records = logFileContent + .split('\n') + .filter(Boolean) + .map((str) => JSON5.parse(str)); + + const unknownDocsWarningLog = records.find((rec) => + rec.message.startsWith(`[.kibana] CHECK_UNKNOWN_DOCUMENTS`) + ); + + expect( + unknownDocsWarningLog.message.startsWith( + '[.kibana] CHECK_UNKNOWN_DOCUMENTS Upgrades will fail for 8.0+ because documents were found for unknown saved ' + + 'object types. To ensure that upgrades will succeed in the future, either re-enable plugins or delete ' + + 'these documents from the ".kibana_8.0.0_001" index after the current upgrade completes.' + ) + ).toBeTruthy(); + + const unknownDocs = [ + { type: 'space', id: 'space:default' }, + { type: 'space', id: 'space:first' }, + { type: 'space', id: 'space:second' }, + { type: 'space', id: 'space:third' }, + { type: 'space', id: 'space:forth' }, + { type: 'space', id: 'space:fifth' }, + { type: 'space', id: 'space:sixth' }, + { type: 'foo', id: 'P2SQfHkBs3dBRGh--No5' }, + { type: 'foo', id: 'QGSZfHkBs3dBRGh-ANoD' }, + { type: 'foo', id: 'QWSZfHkBs3dBRGh-hNob' }, + ]; + + unknownDocs.forEach(({ id, type }) => { + expect(unknownDocsWarningLog.message).toEqual( + expect.stringContaining(`- "${id}" (type: "${type}")`) + ); + }); + + const client: ElasticsearchClient = esServer.es.getClient(); + const { body: response } = await client.indices.getSettings({ index: '.kibana_8.0.0_001' }); + const settings = response['.kibana_8.0.0_001'] + .settings as estypes.IndicesIndexStatePrefixedSettings; + expect(settings.index).not.toBeUndefined(); + expect(settings.index!.blocks?.write).not.toEqual('true'); + + // Ensure that documents for unknown types were preserved in target index in an unmigrated state + const spaceDocs = await fetchDocs(client, '.kibana_8.0.0_001', 'space'); + expect(spaceDocs.map((s) => s.id)).toEqual( + expect.arrayContaining([ + 'space:default', + 'space:first', + 'space:second', + 'space:third', + 'space:forth', + 'space:fifth', + 'space:sixth', + ]) + ); + spaceDocs.forEach((d) => { + expect(d.migrationVersion.space).toEqual('6.6.0'); + expect(d.coreMigrationVersion).toEqual('7.13.0'); + }); + const fooDocs = await fetchDocs(client, '.kibana_8.0.0_001', 'foo'); + expect(fooDocs.map((f) => f.id)).toEqual( + expect.arrayContaining([ + 'P2SQfHkBs3dBRGh--No5', + 'QGSZfHkBs3dBRGh-ANoD', + 'QWSZfHkBs3dBRGh-hNob', + ]) + ); + fooDocs.forEach((d) => { + expect(d.migrationVersion.foo).toEqual('7.13.0'); + expect(d.coreMigrationVersion).toEqual('7.13.0'); + }); + }); + it('migrates outdated documents when types are re-enabled', async () => { + // Start kibana with foo and space types disabled + root = createRoot(); esServer = await startES(); await root.setup(); + await root.start(); - try { - await root.start(); - } catch (err) { - const errorMessage = err.message; - - expect( - errorMessage.startsWith( - 'Unable to complete saved object migrations for the [.kibana] index: Migration failed because documents ' + - 'were found for unknown saved object types. To proceed with the migration, please delete these documents from the ' + - '".kibana_7.13.0_001" index.' - ) - ).toBeTruthy(); - - const unknownDocs = [ - { type: 'space', id: 'space:default' }, - { type: 'space', id: 'space:first' }, - { type: 'space', id: 'space:second' }, - { type: 'space', id: 'space:third' }, - { type: 'space', id: 'space:forth' }, - { type: 'space', id: 'space:fifth' }, - { type: 'space', id: 'space:sixth' }, - { type: 'foo', id: 'P2SQfHkBs3dBRGh--No5' }, - { type: 'foo', id: 'QGSZfHkBs3dBRGh-ANoD' }, - { type: 'foo', id: 'QWSZfHkBs3dBRGh-hNob' }, - ]; - - unknownDocs.forEach(({ id, type }) => { - expect(errorMessage).toEqual(expect.stringContaining(`- "${id}" (type: "${type}")`)); - }); - - const client = esServer.es.getClient(); - const { body: response } = await client.indices.getSettings({ index: '.kibana_7.13.0_001' }); - const settings = response['.kibana_7.13.0_001'] - .settings as estypes.IndicesIndexStatePrefixedSettings; - expect(settings.index).not.toBeUndefined(); - expect(settings.index!.blocks?.write).not.toEqual('true'); - } + // Shutdown and start Kibana again with space type registered to ensure space docs get migrated + await root.shutdown(); + root = createRoot(); + const coreSetup = await root.setup(); + coreSetup.savedObjects.registerType({ + name: 'space', + hidden: false, + mappings: { properties: {} }, + namespaceType: 'agnostic', + migrations: { + '6.6.0': (d) => d, + '8.0.0': (d) => d, + }, + }); + await root.start(); + + const client: ElasticsearchClient = esServer.es.getClient(); + const spacesDocsMigrated = await fetchDocs(client, '.kibana_8.0.0_001', 'space'); + expect(spacesDocsMigrated.map((s) => s.id)).toEqual( + expect.arrayContaining([ + 'space:default', + 'space:first', + 'space:second', + 'space:third', + 'space:forth', + 'space:fifth', + 'space:sixth', + ]) + ); + spacesDocsMigrated.forEach((d) => { + expect(d.migrationVersion.space).toEqual('8.0.0'); // should be migrated + expect(d.coreMigrationVersion).toEqual('8.0.0'); + }); + + // Make sure unmigrated foo docs are also still there in an unmigrated state + const fooDocsUnmigrated = await fetchDocs(client, '.kibana_8.0.0_001', 'foo'); + expect(fooDocsUnmigrated.map((f) => f.id)).toEqual( + expect.arrayContaining([ + 'P2SQfHkBs3dBRGh--No5', + 'QGSZfHkBs3dBRGh-ANoD', + 'QWSZfHkBs3dBRGh-hNob', + ]) + ); + fooDocsUnmigrated.forEach((d) => { + expect(d.migrationVersion.foo).toEqual('7.13.0'); // should still not be migrated + expect(d.coreMigrationVersion).toEqual('7.13.0'); + }); }); }); @@ -131,3 +225,26 @@ function createRoot() { } ); } + +async function fetchDocs(esClient: ElasticsearchClient, index: string, type: string) { + const { body } = await esClient.search({ + index, + size: 10000, + body: { + query: { + bool: { + should: [ + { + term: { type }, + }, + ], + }, + }, + }, + }); + + return body.hits.hits.map((h) => ({ + ...h._source, + id: h._id, + })); +} diff --git a/src/core/server/saved_objects/migrationsv2/migrations_state_action_machine.ts b/src/core/server/saved_objects/migrationsv2/migrations_state_action_machine.ts index 8443f837a7f1d..cd42d4077695e 100644 --- a/src/core/server/saved_objects/migrationsv2/migrations_state_action_machine.ts +++ b/src/core/server/saved_objects/migrationsv2/migrations_state_action_machine.ts @@ -52,6 +52,8 @@ const logStateTransition = ( switch (level) { case 'error': return logger.error(logMessagePrefix + message); + case 'warning': + return logger.warn(logMessagePrefix + message); case 'info': return logger.info(logMessagePrefix + message); default: diff --git a/src/core/server/saved_objects/migrationsv2/model/extract_errors.test.ts b/src/core/server/saved_objects/migrationsv2/model/extract_errors.test.ts index a028c40ca6597..c2daadcd342ac 100644 --- a/src/core/server/saved_objects/migrationsv2/model/extract_errors.test.ts +++ b/src/core/server/saved_objects/migrationsv2/model/extract_errors.test.ts @@ -25,7 +25,7 @@ describe('extractUnknownDocFailureReason', () => { '.kibana_15' ) ).toMatchInlineSnapshot(` - "Migration failed because documents were found for unknown saved object types. To proceed with the migration, please delete these documents from the \\".kibana_15\\" index. + "Upgrades will fail for 8.0+ because documents were found for unknown saved object types. To ensure that upgrades will succeed in the future, either re-enable plugins or delete these documents from the \\".kibana_15\\" index after the current upgrade completes. The documents with unknown types are: - \\"unknownType:12\\" (type: \\"unknownType\\") - \\"anotherUnknownType:42\\" (type: \\"anotherUnknownType\\") diff --git a/src/core/server/saved_objects/migrationsv2/model/extract_errors.ts b/src/core/server/saved_objects/migrationsv2/model/extract_errors.ts index cc6fe7bad3ca7..082e6344afffc 100644 --- a/src/core/server/saved_objects/migrationsv2/model/extract_errors.ts +++ b/src/core/server/saved_objects/migrationsv2/model/extract_errors.ts @@ -38,15 +38,16 @@ export function extractTransformFailuresReason( export function extractUnknownDocFailureReason( unknownDocs: CheckForUnknownDocsFoundDoc[], - sourceIndex: string + targetIndex: string ): string { return ( - `Migration failed because documents were found for unknown saved object types. ` + - `To proceed with the migration, please delete these documents from the "${sourceIndex}" index.\n` + + `Upgrades will fail for 8.0+ because documents were found for unknown saved object types. ` + + `To ensure that upgrades will succeed in the future, either re-enable plugins or delete these documents from the ` + + `"${targetIndex}" index after the current upgrade completes.\n` + `The documents with unknown types are:\n` + unknownDocs.map((doc) => `- "${doc.id}" (type: "${doc.type}")\n`).join('') + `You can delete them using the following command:\n` + - `curl -X POST "{elasticsearch}/${sourceIndex}/_bulk?pretty" -H 'Content-Type: application/json' -d'\n` + + `curl -X POST "{elasticsearch}/${targetIndex}/_bulk?pretty" -H 'Content-Type: application/json' -d'\n` + unknownDocs.map((doc) => `{ "delete" : { "_id" : "${doc.id}" } }\n`).join('') + `'` ); diff --git a/src/core/server/saved_objects/migrationsv2/model/model.test.ts b/src/core/server/saved_objects/migrationsv2/model/model.test.ts index 136709d1b874f..3058f586efb0c 100644 --- a/src/core/server/saved_objects/migrationsv2/model/model.test.ts +++ b/src/core/server/saved_objects/migrationsv2/model/model.test.ts @@ -715,7 +715,7 @@ describe('migrations v2 model', () => { }, } as const; - test('CHECK_UNKNOWN_DOCUMENTS -> SET_SOURCE_WRITE_BLOCK if action succeeds', () => { + test('CHECK_UNKNOWN_DOCUMENTS -> SET_SOURCE_WRITE_BLOCK if action succeeds and no unknown docs are found', () => { const checkUnknownDocumentsSourceState: CheckUnknownDocumentsState = { ...baseState, controlState: 'CHECK_UNKNOWN_DOCUMENTS', @@ -723,7 +723,7 @@ describe('migrations v2 model', () => { sourceIndexMappings: mappingsWithUnknownType, }; - const res: ResponseType<'CHECK_UNKNOWN_DOCUMENTS'> = Either.right({}); + const res: ResponseType<'CHECK_UNKNOWN_DOCUMENTS'> = Either.right({ unknownDocs: [] }); const newState = model(checkUnknownDocumentsSourceState, res); expect(newState.controlState).toEqual('SET_SOURCE_WRITE_BLOCK'); @@ -758,9 +758,12 @@ describe('migrations v2 model', () => { }, } `); + + // No log message gets appended + expect(newState.logs).toEqual([]); }); - test('CHECK_UNKNOWN_DOCUMENTS -> FATAL if action fails and unknown docs were found', () => { + test('CHECK_UNKNOWN_DOCUMENTS -> SET_SOURCE_WRITE_BLOCK and adds log if action succeeds and unknown docs were found', () => { const checkUnknownDocumentsSourceState: CheckUnknownDocumentsState = { ...baseState, controlState: 'CHECK_UNKNOWN_DOCUMENTS', @@ -768,20 +771,51 @@ describe('migrations v2 model', () => { sourceIndexMappings: mappingsWithUnknownType, }; - const res: ResponseType<'CHECK_UNKNOWN_DOCUMENTS'> = Either.left({ - type: 'unknown_docs_found', + const res: ResponseType<'CHECK_UNKNOWN_DOCUMENTS'> = Either.right({ unknownDocs: [ { id: 'dashboard:12', type: 'dashboard' }, { id: 'foo:17', type: 'foo' }, ], }); const newState = model(checkUnknownDocumentsSourceState, res); - expect(newState.controlState).toEqual('FATAL'); + expect(newState.controlState).toEqual('SET_SOURCE_WRITE_BLOCK'); expect(newState).toMatchObject({ - controlState: 'FATAL', - reason: expect.stringContaining( - 'Migration failed because documents were found for unknown saved object types' + controlState: 'SET_SOURCE_WRITE_BLOCK', + sourceIndex: Option.some('.kibana_3'), + targetIndex: '.kibana_7.11.0_001', + }); + + // This snapshot asserts that we disable the unknown saved object + // type. Because it's mappings are disabled, we also don't copy the + // `_meta.migrationMappingPropertyHashes` for the disabled type. + expect(newState.targetIndexMappings).toMatchInlineSnapshot(` + Object { + "_meta": Object { + "migrationMappingPropertyHashes": Object { + "new_saved_object_type": "4a11183eee21e6fbad864f7a30b39ad0", + }, + }, + "properties": Object { + "disabled_saved_object_type": Object { + "dynamic": false, + "properties": Object {}, + }, + "new_saved_object_type": Object { + "properties": Object { + "value": Object { + "type": "text", + }, + }, + }, + }, + } + `); + + expect(newState.logs[0]).toMatchObject({ + level: 'warning', + message: expect.stringContaining( + 'Upgrades will fail for 8.0+ because documents were found for unknown saved object types' ), }); }); diff --git a/src/core/server/saved_objects/migrationsv2/model/model.ts b/src/core/server/saved_objects/migrationsv2/model/model.ts index b28e4e3024380..a78457fa891f7 100644 --- a/src/core/server/saved_objects/migrationsv2/model/model.ts +++ b/src/core/server/saved_objects/migrationsv2/model/model.ts @@ -10,7 +10,7 @@ import * as Either from 'fp-ts/lib/Either'; import * as Option from 'fp-ts/lib/Option'; import { AliasAction, isLeftTypeof } from '../actions'; -import { AllActionStates, State } from '../types'; +import { AllActionStates, MigrationLog, State } from '../types'; import type { ResponseType } from '../next'; import { disableUnknownTypeMappingFields } from '../../migrations/core/migration_context'; import { @@ -318,6 +318,7 @@ export const model = (currentState: State, resW: ResponseType): } } else if (stateP.controlState === 'CHECK_UNKNOWN_DOCUMENTS') { const res = resW as ExcludeRetryableEsError>; + if (Either.isRight(res)) { const source = stateP.sourceIndex; const target = stateP.versionIndex; @@ -336,17 +337,24 @@ export const model = (currentState: State, resW: ResponseType): { add: { index: target, alias: stateP.versionAlias } }, { remove_index: { index: stateP.tempIndex } }, ]), + + logs: [ + ...stateP.logs, + ...(res.right.unknownDocs.length > 0 + ? ([ + { + level: 'warning', + message: `CHECK_UNKNOWN_DOCUMENTS ${extractUnknownDocFailureReason( + res.right.unknownDocs, + target + )}`, + }, + ] as MigrationLog[]) + : []), + ], }; } else { - if (isLeftTypeof(res.left, 'unknown_docs_found')) { - return { - ...stateP, - controlState: 'FATAL', - reason: extractUnknownDocFailureReason(res.left.unknownDocs, stateP.sourceIndex.value), - }; - } else { - return throwBadResponse(stateP, res.left); - } + return throwBadResponse(stateP, res); } } else if (stateP.controlState === 'SET_SOURCE_WRITE_BLOCK') { const res = resW as ExcludeRetryableEsError>; diff --git a/src/core/server/saved_objects/migrationsv2/types.ts b/src/core/server/saved_objects/migrationsv2/types.ts index ed361a710ac99..576e3a4412184 100644 --- a/src/core/server/saved_objects/migrationsv2/types.ts +++ b/src/core/server/saved_objects/migrationsv2/types.ts @@ -19,7 +19,7 @@ import { DocumentsTransformSuccess, } from '../migrations/core/migrate_raw_docs'; -export type MigrationLogLevel = 'error' | 'info'; +export type MigrationLogLevel = 'error' | 'info' | 'warning'; export interface MigrationLog { level: MigrationLogLevel; diff --git a/src/dev/license_checker/config.ts b/src/dev/license_checker/config.ts index b3b7bf5e8eed7..893061c8e1b89 100644 --- a/src/dev/license_checker/config.ts +++ b/src/dev/license_checker/config.ts @@ -74,6 +74,7 @@ export const LICENSE_OVERRIDES = { 'jsts@1.6.2': ['Eclipse Distribution License - v 1.0'], // cf. https://github.com/bjornharrtell/jsts '@mapbox/jsonlint-lines-primitives@2.0.2': ['MIT'], // license in readme https://github.com/tmcw/jsonlint '@elastic/ems-client@7.14.0': ['Elastic License 2.0'], + '@elastic/eui@35.0.0': ['SSPL-1.0 OR Elastic License 2.0'], // TODO can be removed if the https://github.com/jindw/xmldom/issues/239 is released 'xmldom@0.1.27': ['MIT'], diff --git a/src/dev/precommit_hook/casing_check_config.js b/src/dev/precommit_hook/casing_check_config.js index 57ae640da3c84..a71013cb06a88 100644 --- a/src/dev/precommit_hook/casing_check_config.js +++ b/src/dev/precommit_hook/casing_check_config.js @@ -40,6 +40,7 @@ export const IGNORE_FILE_GLOBS = [ 'vars/*', '.ci/pipeline-library/**/*', 'packages/kbn-test/jest-preset.js', + 'packages/kbn-test/jest_integration/jest-preset.js', 'test/package/Vagrantfile', '**/test/**/fixtures/**/*', diff --git a/src/dev/storybook/aliases.ts b/src/dev/storybook/aliases.ts index 6fc0841551fad..15497258d4574 100644 --- a/src/dev/storybook/aliases.ts +++ b/src/dev/storybook/aliases.ts @@ -17,6 +17,7 @@ export const storybookAliases = { dashboard_enhanced: 'x-pack/plugins/dashboard_enhanced/.storybook', data_enhanced: 'x-pack/plugins/data_enhanced/.storybook', embeddable: 'src/plugins/embeddable/.storybook', + expression_error: 'src/plugins/expression_error/.storybook', expression_reveal_image: 'src/plugins/expression_reveal_image/.storybook', infra: 'x-pack/plugins/infra/.storybook', security_solution: 'x-pack/plugins/security_solution/.storybook', diff --git a/src/plugins/console/server/lib/spec_definitions/js/ingest.ts b/src/plugins/console/server/lib/spec_definitions/js/ingest.ts index 2ac8d2cb87ccc..5ffae9d7be23e 100644 --- a/src/plugins/console/server/lib/spec_definitions/js/ingest.ts +++ b/src/plugins/console/server/lib/spec_definitions/js/ingest.ts @@ -179,6 +179,29 @@ const dropProcessorDefinition = { }, }; +// Based on https://www.elastic.co/guide/en/elasticsearch/reference/master/enrich-processor.html +const enrichProcessorDefinition = { + enrich: { + __template: { + policy_name: '', + field: '', + target_field: '', + }, + policy_name: '', + field: '', + target_field: '', + ignore_missing: { + __one_of: [false, true], + }, + override: { + __one_of: [true, false], + }, + max_matches: 1, + shape_relation: 'INTERSECTS', + ...commonPipelineParams, + }, +}; + // Based on https://www.elastic.co/guide/en/elasticsearch/reference/master/fail-processor.html const failProcessorDefinition = { fail: { @@ -279,13 +302,26 @@ const inferenceProcessorDefinition = { inference: { __template: { model_id: '', - field_map: {}, inference_config: {}, + field_mappings: {}, }, - model_id: '', - field_map: {}, - inference_config: {}, target_field: '', + model_id: '', + field_mappings: { + __template: {}, + }, + inference_config: { + regression: { + __template: {}, + results_field: '', + }, + classification: { + __template: {}, + results_field: '', + num_top_classes: 2, + top_classes_results_field: '', + }, + }, ...commonPipelineParams, }, }; @@ -530,6 +566,7 @@ const processorDefinition = { dissectProcessorDefinition, dotExpanderProcessorDefinition, dropProcessorDefinition, + enrichProcessorDefinition, failProcessorDefinition, foreachProcessorDefinition, geoipProcessorDefinition, diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/async_search.delete.json b/src/plugins/console/server/lib/spec_definitions/json/generated/async_search.delete.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/async_search.delete.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/async_search.delete.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/async_search.get.json b/src/plugins/console/server/lib/spec_definitions/json/generated/async_search.get.json similarity index 88% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/async_search.get.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/async_search.get.json index 09f4520d580e3..3fb1f3da6fdf7 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/async_search.get.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/async_search.get.json @@ -1,7 +1,7 @@ { "async_search.get": { "url_params": { - "wait_for_completion": "", + "wait_for_completion_timeout": "", "keep_alive": "", "typed_keys": "__flag__" }, diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/async_search.status.json b/src/plugins/console/server/lib/spec_definitions/json/generated/async_search.status.json new file mode 100644 index 0000000000000..f2aef917ea23d --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/async_search.status.json @@ -0,0 +1,11 @@ +{ + "async_search.status": { + "methods": [ + "GET" + ], + "patterns": [ + "_async_search/status/{id}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html" + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/async_search.submit.json b/src/plugins/console/server/lib/spec_definitions/json/generated/async_search.submit.json similarity index 92% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/async_search.submit.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/async_search.submit.json index 83fb7c0fe75ad..635436bdb167f 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/async_search.submit.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/async_search.submit.json @@ -1,8 +1,8 @@ { "async_search.submit": { "url_params": { - "wait_for_completion": "", - "clean_on_completion": "__flag__", + "wait_for_completion_timeout": "", + "keep_on_completion": "__flag__", "keep_alive": "", "batched_reduce_size": "", "request_cache": "__flag__", @@ -23,6 +23,7 @@ "expand_wildcards": [ "open", "closed", + "hidden", "none", "all" ], @@ -51,7 +52,7 @@ "suggest_text": "", "timeout": "", "track_scores": "__flag__", - "track_total_hits": "__flag__", + "track_total_hits": "", "allow_partial_search_results": "__flag__", "typed_keys": "__flag__", "version": "__flag__", diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/autoscaling.delete_autoscaling_policy.json b/src/plugins/console/server/lib/spec_definitions/json/generated/autoscaling.delete_autoscaling_policy.json new file mode 100644 index 0000000000000..2d65c9744987c --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/autoscaling.delete_autoscaling_policy.json @@ -0,0 +1,11 @@ +{ + "autoscaling.delete_autoscaling_policy": { + "methods": [ + "DELETE" + ], + "patterns": [ + "_autoscaling/policy/{name}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/autoscaling-delete-autoscaling-policy.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/autoscaling.get_autoscaling_capacity.json b/src/plugins/console/server/lib/spec_definitions/json/generated/autoscaling.get_autoscaling_capacity.json new file mode 100644 index 0000000000000..24aea8cfba86b --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/autoscaling.get_autoscaling_capacity.json @@ -0,0 +1,11 @@ +{ + "autoscaling.get_autoscaling_capacity": { + "methods": [ + "GET" + ], + "patterns": [ + "_autoscaling/capacity" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/autoscaling-get-autoscaling-capacity.html" + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/autoscaling.get_autoscaling_decision.json b/src/plugins/console/server/lib/spec_definitions/json/generated/autoscaling.get_autoscaling_decision.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/autoscaling.get_autoscaling_decision.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/autoscaling.get_autoscaling_decision.json diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/autoscaling.get_autoscaling_policy.json b/src/plugins/console/server/lib/spec_definitions/json/generated/autoscaling.get_autoscaling_policy.json new file mode 100644 index 0000000000000..4d5ab1d00266b --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/autoscaling.get_autoscaling_policy.json @@ -0,0 +1,11 @@ +{ + "autoscaling.get_autoscaling_policy": { + "methods": [ + "GET" + ], + "patterns": [ + "_autoscaling/policy/{name}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/autoscaling-get-autoscaling-policy.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/autoscaling.put_autoscaling_policy.json b/src/plugins/console/server/lib/spec_definitions/json/generated/autoscaling.put_autoscaling_policy.json new file mode 100644 index 0000000000000..3ed40851c94d9 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/autoscaling.put_autoscaling_policy.json @@ -0,0 +1,11 @@ +{ + "autoscaling.put_autoscaling_policy": { + "methods": [ + "PUT" + ], + "patterns": [ + "_autoscaling/policy/{name}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/autoscaling-put-autoscaling-policy.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/bulk.json b/src/plugins/console/server/lib/spec_definitions/json/generated/bulk.json index 2d3bd260372ba..144a3ad359c40 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/bulk.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/bulk.json @@ -13,7 +13,8 @@ "_source": [], "_source_excludes": [], "_source_includes": [], - "pipeline": "" + "pipeline": "", + "require_alias": "__flag__" }, "methods": [ "POST", diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/cat.indices.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.indices.json index 410350df13721..a0d405f60904d 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/cat.indices.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.indices.json @@ -15,7 +15,6 @@ "p", "pb" ], - "local": "__flag__", "master_timeout": "", "h": [], "health": [ diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/cat.ml_data_frame_analytics.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.ml_data_frame_analytics.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/cat.ml_data_frame_analytics.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.ml_data_frame_analytics.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/cat.ml_datafeeds.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.ml_datafeeds.json similarity index 93% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/cat.ml_datafeeds.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.ml_datafeeds.json index 04f4e45782e1f..e58735a037153 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/cat.ml_datafeeds.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.ml_datafeeds.json @@ -1,6 +1,7 @@ { "cat.ml_datafeeds": { "url_params": { + "allow_no_match": "__flag__", "allow_no_datafeeds": "__flag__", "format": "", "h": [], diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/cat.ml_jobs.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.ml_jobs.json similarity index 95% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/cat.ml_jobs.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.ml_jobs.json index 2f7e03e564b5d..a7a068fa3691e 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/cat.ml_jobs.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.ml_jobs.json @@ -1,6 +1,7 @@ { "cat.ml_jobs": { "url_params": { + "allow_no_match": "__flag__", "allow_no_jobs": "__flag__", "bytes": [ "b", diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/cat.ml_trained_models.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.ml_trained_models.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/cat.ml_trained_models.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.ml_trained_models.json diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/cat.nodes.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.nodes.json index 2e89a66ef1f35..b695bb1df9625 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/cat.nodes.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.nodes.json @@ -16,7 +16,6 @@ ], "format": "", "full_id": "__flag__", - "local": "__flag__", "master_timeout": "", "h": [], "help": "__flag__", @@ -30,7 +29,8 @@ "micros", "nanos" ], - "v": "__flag__" + "v": "__flag__", + "include_unloaded_segments": "__flag__" }, "methods": [ "GET" diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/cat.plugins.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.plugins.json index 958992be81216..752313bf6f975 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/cat.plugins.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.plugins.json @@ -6,6 +6,7 @@ "master_timeout": "", "h": [], "help": "__flag__", + "include_bootstrap": "__flag__", "s": [], "v": "__flag__" }, diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/cat.shards.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.shards.json index 2b66562e3d6df..64a235eae67d5 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/cat.shards.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.shards.json @@ -15,7 +15,6 @@ "p", "pb" ], - "local": "__flag__", "master_timeout": "", "h": [], "help": "__flag__", diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/cat.tasks.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.tasks.json index 31c987a5893cf..f981d189a17b2 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/cat.tasks.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.tasks.json @@ -2,10 +2,10 @@ "cat.tasks": { "url_params": { "format": "", - "node_id": [], + "nodes": [], "actions": [], "detailed": "__flag__", - "parent_task": "", + "parent_task_id": "", "h": [], "help": "__flag__", "s": [], diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/cat.thread_pool.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.thread_pool.json index 5017a0390a2e4..d237f8d54ab2a 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/cat.thread_pool.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.thread_pool.json @@ -2,13 +2,14 @@ "cat.thread_pool": { "url_params": { "format": "", - "size": [ - "", - "k", + "time": [ + "d", + "h", "m", - "g", - "t", - "p" + "s", + "ms", + "micros", + "nanos" ], "local": "__flag__", "master_timeout": "", diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/cat.transforms.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.transforms.json new file mode 100644 index 0000000000000..048d7af411635 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.transforms.json @@ -0,0 +1,31 @@ +{ + "cat.transforms": { + "url_params": { + "from": 0, + "size": 0, + "allow_no_match": "__flag__", + "format": "", + "h": [], + "help": "__flag__", + "s": [], + "time": [ + "d", + "h", + "m", + "s", + "ms", + "micros", + "nanos" + ], + "v": "__flag__" + }, + "methods": [ + "GET" + ], + "patterns": [ + "_cat/transforms", + "_cat/transforms/{transform_id}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/cat-transforms.html" + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ccr.delete_auto_follow_pattern.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ccr.delete_auto_follow_pattern.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ccr.delete_auto_follow_pattern.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ccr.delete_auto_follow_pattern.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ccr.follow.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ccr.follow.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ccr.follow.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ccr.follow.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ccr.follow_info.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ccr.follow_info.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ccr.follow_info.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ccr.follow_info.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ccr.follow_stats.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ccr.follow_stats.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ccr.follow_stats.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ccr.follow_stats.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ccr.forget_follower.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ccr.forget_follower.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ccr.forget_follower.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ccr.forget_follower.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ccr.get_auto_follow_pattern.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ccr.get_auto_follow_pattern.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ccr.get_auto_follow_pattern.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ccr.get_auto_follow_pattern.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ccr.pause_auto_follow_pattern.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ccr.pause_auto_follow_pattern.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ccr.pause_auto_follow_pattern.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ccr.pause_auto_follow_pattern.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ccr.pause_follow.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ccr.pause_follow.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ccr.pause_follow.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ccr.pause_follow.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ccr.put_auto_follow_pattern.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ccr.put_auto_follow_pattern.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ccr.put_auto_follow_pattern.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ccr.put_auto_follow_pattern.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ccr.resume_auto_follow_pattern.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ccr.resume_auto_follow_pattern.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ccr.resume_auto_follow_pattern.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ccr.resume_auto_follow_pattern.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ccr.resume_follow.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ccr.resume_follow.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ccr.resume_follow.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ccr.resume_follow.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ccr.stats.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ccr.stats.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ccr.stats.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ccr.stats.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ccr.unfollow.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ccr.unfollow.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ccr.unfollow.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ccr.unfollow.json diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/clear_scroll.json b/src/plugins/console/server/lib/spec_definitions/json/generated/clear_scroll.json index 7e6e6692f931b..5273574357ddd 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/clear_scroll.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/clear_scroll.json @@ -6,6 +6,6 @@ "patterns": [ "_search/scroll" ], - "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/search-request-body.html#_clear_scroll_api" + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/clear-scroll-api.html" } } diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/close_point_in_time.json b/src/plugins/console/server/lib/spec_definitions/json/generated/close_point_in_time.json new file mode 100644 index 0000000000000..e1997bc2e20ea --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/close_point_in_time.json @@ -0,0 +1,11 @@ +{ + "close_point_in_time": { + "methods": [ + "DELETE" + ], + "patterns": [ + "_pit" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/point-in-time-api.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.delete_component_template.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.delete_component_template.json index e935b8999e6d3..24255f7231892 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.delete_component_template.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.delete_component_template.json @@ -10,6 +10,6 @@ "patterns": [ "_component_template/{name}" ], - "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-component-templates.html" + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-component-template.html" } } diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.delete_voting_config_exclusions.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.delete_voting_config_exclusions.json new file mode 100644 index 0000000000000..6b4b48113f2a8 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.delete_voting_config_exclusions.json @@ -0,0 +1,14 @@ +{ + "cluster.delete_voting_config_exclusions": { + "url_params": { + "wait_for_removal": "__flag__" + }, + "methods": [ + "DELETE" + ], + "patterns": [ + "_cluster/voting_config_exclusions" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/voting-config-exclusions.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.exists_component_template.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.exists_component_template.json new file mode 100644 index 0000000000000..24dcbeb006e6f --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.exists_component_template.json @@ -0,0 +1,15 @@ +{ + "cluster.exists_component_template": { + "url_params": { + "master_timeout": "", + "local": "__flag__" + }, + "methods": [ + "HEAD" + ], + "patterns": [ + "_component_template/{name}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-component-template.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.get_component_template.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.get_component_template.json index 784053d69ad5a..cbfed6741f8a4 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.get_component_template.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.get_component_template.json @@ -12,5 +12,6 @@ "_component_template", "_component_template/{name}" ], - "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/getting-component-templates.html" } + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/getting-component-templates.html" + } } diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.get_settings.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.get_settings.json index 249f582c33682..6c97254b5201c 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.get_settings.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.get_settings.json @@ -12,6 +12,6 @@ "patterns": [ "_cluster/settings" ], - "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/cluster-update-settings.html" + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/cluster-get-settings.html" } } diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.post_voting_config_exclusions.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.post_voting_config_exclusions.json new file mode 100644 index 0000000000000..74cc74a8c05a4 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.post_voting_config_exclusions.json @@ -0,0 +1,16 @@ +{ + "cluster.post_voting_config_exclusions": { + "url_params": { + "node_ids": "", + "node_names": "", + "timeout": "" + }, + "methods": [ + "POST" + ], + "patterns": [ + "_cluster/voting_config_exclusions" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/voting-config-exclusions.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.put_component_template.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.put_component_template.json index 8a0bfc278cbf3..999ff0c149fe8 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.put_component_template.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.put_component_template.json @@ -5,10 +5,12 @@ "master_timeout": "" }, "methods": [ - "PUT" + "PUT", + "POST" ], "patterns": [ "_component_template/{name}" ], - "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-component-template.html" } + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-component-template.html" + } } diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/dangling_indices.delete_dangling_index.json b/src/plugins/console/server/lib/spec_definitions/json/generated/dangling_indices.delete_dangling_index.json new file mode 100644 index 0000000000000..03d7c914baab5 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/dangling_indices.delete_dangling_index.json @@ -0,0 +1,16 @@ +{ + "dangling_indices.delete_dangling_index": { + "url_params": { + "accept_data_loss": "__flag__", + "timeout": "", + "master_timeout": "" + }, + "methods": [ + "DELETE" + ], + "patterns": [ + "_dangling/{index_uuid}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/modules-gateway-dangling-indices.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/dangling_indices.import_dangling_index.json b/src/plugins/console/server/lib/spec_definitions/json/generated/dangling_indices.import_dangling_index.json new file mode 100644 index 0000000000000..c55cdd2901699 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/dangling_indices.import_dangling_index.json @@ -0,0 +1,16 @@ +{ + "dangling_indices.import_dangling_index": { + "url_params": { + "accept_data_loss": "__flag__", + "timeout": "", + "master_timeout": "" + }, + "methods": [ + "POST" + ], + "patterns": [ + "_dangling/{index_uuid}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/modules-gateway-dangling-indices.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/dangling_indices.list_dangling_indices.json b/src/plugins/console/server/lib/spec_definitions/json/generated/dangling_indices.list_dangling_indices.json new file mode 100644 index 0000000000000..63dd560947876 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/dangling_indices.list_dangling_indices.json @@ -0,0 +1,11 @@ +{ + "dangling_indices.list_dangling_indices": { + "methods": [ + "GET" + ], + "patterns": [ + "_dangling" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/modules-gateway-dangling-indices.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/data_frame_transform_deprecated.delete_transform.json b/src/plugins/console/server/lib/spec_definitions/json/generated/data_frame_transform_deprecated.delete_transform.json new file mode 100644 index 0000000000000..6dfd81ded7bc5 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/data_frame_transform_deprecated.delete_transform.json @@ -0,0 +1,10 @@ +{ + "data_frame_transform_deprecated.delete_transform": { + "url_params": { + "force": "__flag__" + }, + "methods": [], + "patterns": [], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/delete-transform.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/data_frame_transform_deprecated.get_transform.json b/src/plugins/console/server/lib/spec_definitions/json/generated/data_frame_transform_deprecated.get_transform.json new file mode 100644 index 0000000000000..be3716ba3da6e --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/data_frame_transform_deprecated.get_transform.json @@ -0,0 +1,13 @@ +{ + "data_frame_transform_deprecated.get_transform": { + "url_params": { + "from": 0, + "size": 0, + "allow_no_match": "__flag__", + "exclude_generated": "__flag__" + }, + "methods": [], + "patterns": [], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/get-transform.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/data_frame_transform_deprecated.get_transform_stats.json b/src/plugins/console/server/lib/spec_definitions/json/generated/data_frame_transform_deprecated.get_transform_stats.json new file mode 100644 index 0000000000000..440eed6a1f2a4 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/data_frame_transform_deprecated.get_transform_stats.json @@ -0,0 +1,12 @@ +{ + "data_frame_transform_deprecated.get_transform_stats": { + "url_params": { + "from": "", + "size": "", + "allow_no_match": "__flag__" + }, + "methods": [], + "patterns": [], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/get-transform-stats.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/data_frame_transform_deprecated.preview_transform.json b/src/plugins/console/server/lib/spec_definitions/json/generated/data_frame_transform_deprecated.preview_transform.json new file mode 100644 index 0000000000000..f095847ed0d9a --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/data_frame_transform_deprecated.preview_transform.json @@ -0,0 +1,7 @@ +{ + "data_frame_transform_deprecated.preview_transform": { + "methods": [], + "patterns": [], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/preview-transform.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/data_frame_transform_deprecated.put_transform.json b/src/plugins/console/server/lib/spec_definitions/json/generated/data_frame_transform_deprecated.put_transform.json new file mode 100644 index 0000000000000..e7555ce4bad29 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/data_frame_transform_deprecated.put_transform.json @@ -0,0 +1,10 @@ +{ + "data_frame_transform_deprecated.put_transform": { + "url_params": { + "defer_validation": "__flag__" + }, + "methods": [], + "patterns": [], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/put-transform.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/data_frame_transform_deprecated.start_transform.json b/src/plugins/console/server/lib/spec_definitions/json/generated/data_frame_transform_deprecated.start_transform.json new file mode 100644 index 0000000000000..d50346ff328c6 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/data_frame_transform_deprecated.start_transform.json @@ -0,0 +1,10 @@ +{ + "data_frame_transform_deprecated.start_transform": { + "url_params": { + "timeout": "" + }, + "methods": [], + "patterns": [], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/start-transform.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/data_frame_transform_deprecated.stop_transform.json b/src/plugins/console/server/lib/spec_definitions/json/generated/data_frame_transform_deprecated.stop_transform.json new file mode 100644 index 0000000000000..2618446916c76 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/data_frame_transform_deprecated.stop_transform.json @@ -0,0 +1,12 @@ +{ + "data_frame_transform_deprecated.stop_transform": { + "url_params": { + "wait_for_completion": "__flag__", + "timeout": "", + "allow_no_match": "__flag__" + }, + "methods": [], + "patterns": [], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/stop-transform.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/data_frame_transform_deprecated.update_transform.json b/src/plugins/console/server/lib/spec_definitions/json/generated/data_frame_transform_deprecated.update_transform.json new file mode 100644 index 0000000000000..60f71474e865a --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/data_frame_transform_deprecated.update_transform.json @@ -0,0 +1,10 @@ +{ + "data_frame_transform_deprecated.update_transform": { + "url_params": { + "defer_validation": "__flag__" + }, + "methods": [], + "patterns": [], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/update-transform.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/delete.json b/src/plugins/console/server/lib/spec_definitions/json/generated/delete.json index 0852d8d184831..a71a659618eb1 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/delete.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/delete.json @@ -15,8 +15,7 @@ "version_type": [ "internal", "external", - "external_gte", - "force" + "external_gte" ] }, "methods": [ diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/delete_by_query.json b/src/plugins/console/server/lib/spec_definitions/json/generated/delete_by_query.json index e01ea8b2dec6d..4e2b3a180fbba 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/delete_by_query.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/delete_by_query.json @@ -32,7 +32,6 @@ "dfs_query_then_fetch" ], "search_timeout": "", - "size": "", "max_docs": "all documents", "sort": [], "_source": [], diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/enrich.delete_policy.json b/src/plugins/console/server/lib/spec_definitions/json/generated/enrich.delete_policy.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/enrich.delete_policy.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/enrich.delete_policy.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/enrich.execute_policy.json b/src/plugins/console/server/lib/spec_definitions/json/generated/enrich.execute_policy.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/enrich.execute_policy.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/enrich.execute_policy.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/enrich.get_policy.json b/src/plugins/console/server/lib/spec_definitions/json/generated/enrich.get_policy.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/enrich.get_policy.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/enrich.get_policy.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/enrich.put_policy.json b/src/plugins/console/server/lib/spec_definitions/json/generated/enrich.put_policy.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/enrich.put_policy.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/enrich.put_policy.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/enrich.stats.json b/src/plugins/console/server/lib/spec_definitions/json/generated/enrich.stats.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/enrich.stats.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/enrich.stats.json diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/eql.delete.json b/src/plugins/console/server/lib/spec_definitions/json/generated/eql.delete.json new file mode 100644 index 0000000000000..4bc4846f628a0 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/eql.delete.json @@ -0,0 +1,11 @@ +{ + "eql.delete": { + "methods": [ + "DELETE" + ], + "patterns": [ + "_eql/search/{id}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/eql-search-api.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/eql.get.json b/src/plugins/console/server/lib/spec_definitions/json/generated/eql.get.json new file mode 100644 index 0000000000000..4aa740c333beb --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/eql.get.json @@ -0,0 +1,15 @@ +{ + "eql.get": { + "url_params": { + "wait_for_completion_timeout": "", + "keep_alive": "" + }, + "methods": [ + "GET" + ], + "patterns": [ + "_eql/search/{id}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/eql-search-api.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/eql.get_status.json b/src/plugins/console/server/lib/spec_definitions/json/generated/eql.get_status.json new file mode 100644 index 0000000000000..68fe8aff70046 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/eql.get_status.json @@ -0,0 +1,11 @@ +{ + "eql.get_status": { + "methods": [ + "GET" + ], + "patterns": [ + "_eql/search/status/{id}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/get-async-eql-status-api.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/eql.search.json b/src/plugins/console/server/lib/spec_definitions/json/generated/eql.search.json new file mode 100644 index 0000000000000..67e7a1729c295 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/eql.search.json @@ -0,0 +1,17 @@ +{ + "eql.search": { + "url_params": { + "wait_for_completion_timeout": "", + "keep_on_completion": "__flag__", + "keep_alive": "" + }, + "methods": [ + "GET", + "POST" + ], + "patterns": [ + "{indices}/_eql/search" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/eql-search-api.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/exists.json b/src/plugins/console/server/lib/spec_definitions/json/generated/exists.json index a6799b9e361cd..7d66dad050e5d 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/exists.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/exists.json @@ -13,8 +13,7 @@ "version_type": [ "internal", "external", - "external_gte", - "force" + "external_gte" ] }, "methods": [ diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/exists_source.json b/src/plugins/console/server/lib/spec_definitions/json/generated/exists_source.json index 9ffc4b55f3037..6811940cb0f1e 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/exists_source.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/exists_source.json @@ -12,8 +12,7 @@ "version_type": [ "internal", "external", - "external_gte", - "force" + "external_gte" ] }, "methods": [ diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/features.get_features.json b/src/plugins/console/server/lib/spec_definitions/json/generated/features.get_features.json new file mode 100644 index 0000000000000..abbf74b880f1a --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/features.get_features.json @@ -0,0 +1,14 @@ +{ + "features.get_features": { + "url_params": { + "master_timeout": "" + }, + "methods": [ + "GET" + ], + "patterns": [ + "_features" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/get-features-api.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/features.reset_features.json b/src/plugins/console/server/lib/spec_definitions/json/generated/features.reset_features.json new file mode 100644 index 0000000000000..98280afdab1a0 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/features.reset_features.json @@ -0,0 +1,11 @@ +{ + "features.reset_features": { + "methods": [ + "POST" + ], + "patterns": [ + "_features/_reset" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/reset-features-api.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/fleet.global_checkpoints.json b/src/plugins/console/server/lib/spec_definitions/json/generated/fleet.global_checkpoints.json new file mode 100644 index 0000000000000..29f37c9099de2 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/fleet.global_checkpoints.json @@ -0,0 +1,17 @@ +{ + "fleet.global_checkpoints": { + "url_params": { + "wait_for_advance": "__flag__", + "wait_for_index": "__flag__", + "checkpoints": [], + "timeout": "" + }, + "methods": [ + "GET" + ], + "patterns": [ + "{indices}/_fleet/global_checkpoints" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/get-global-checkpoints.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/get.json b/src/plugins/console/server/lib/spec_definitions/json/generated/get.json index 5a72761c7c32e..396396f7550ca 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/get.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/get.json @@ -13,8 +13,7 @@ "version_type": [ "internal", "external", - "external_gte", - "force" + "external_gte" ] }, "methods": [ diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/get_script_context.json b/src/plugins/console/server/lib/spec_definitions/json/generated/get_script_context.json index 528c261df7707..f3808d568e989 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/get_script_context.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/get_script_context.json @@ -5,6 +5,7 @@ ], "patterns": [ "_script_context" - ] + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/painless/master/painless-contexts.html" } } diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/get_script_languages.json b/src/plugins/console/server/lib/spec_definitions/json/generated/get_script_languages.json index 10ea433ca68c5..a872885615d05 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/get_script_languages.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/get_script_languages.json @@ -5,6 +5,7 @@ ], "patterns": [ "_script_language" - ] + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/modules-scripting.html" } } diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/get_source.json b/src/plugins/console/server/lib/spec_definitions/json/generated/get_source.json index 8201960363a78..de1b5b0f3de21 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/get_source.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/get_source.json @@ -12,8 +12,7 @@ "version_type": [ "internal", "external", - "external_gte", - "force" + "external_gte" ] }, "methods": [ diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/graph.explore.json b/src/plugins/console/server/lib/spec_definitions/json/generated/graph.explore.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/graph.explore.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/graph.explore.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ilm.delete_lifecycle.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ilm.delete_lifecycle.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ilm.delete_lifecycle.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ilm.delete_lifecycle.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ilm.explain_lifecycle.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ilm.explain_lifecycle.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ilm.explain_lifecycle.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ilm.explain_lifecycle.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ilm.get_lifecycle.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ilm.get_lifecycle.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ilm.get_lifecycle.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ilm.get_lifecycle.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ilm.get_status.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ilm.get_status.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ilm.get_status.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ilm.get_status.json diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/ilm.migrate_to_data_tiers.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ilm.migrate_to_data_tiers.json new file mode 100644 index 0000000000000..acafe1183a893 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ilm.migrate_to_data_tiers.json @@ -0,0 +1,14 @@ +{ + "ilm.migrate_to_data_tiers": { + "url_params": { + "dry_run": "__flag__" + }, + "methods": [ + "POST" + ], + "patterns": [ + "_ilm/migrate_to_data_tiers" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/ilm-migrate-to-data-tiers.html" + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ilm.move_to_step.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ilm.move_to_step.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ilm.move_to_step.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ilm.move_to_step.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ilm.put_lifecycle.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ilm.put_lifecycle.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ilm.put_lifecycle.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ilm.put_lifecycle.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ilm.remove_policy.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ilm.remove_policy.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ilm.remove_policy.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ilm.remove_policy.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ilm.retry.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ilm.retry.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ilm.retry.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ilm.retry.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ilm.set_policy.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ilm.set_policy.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ilm.set_policy.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ilm.set_policy.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ilm.start.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ilm.start.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ilm.start.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ilm.start.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ilm.stop.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ilm.stop.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ilm.stop.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ilm.stop.json diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/index.json b/src/plugins/console/server/lib/spec_definitions/json/generated/index.json index 25977806776a7..c6b290a58812b 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/index.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/index.json @@ -21,7 +21,8 @@ ], "if_seq_no": "", "if_primary_term": "", - "pipeline": "" + "pipeline": "", + "require_alias": "__flag__" }, "methods": [ "PUT", diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.add_block.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.add_block.json new file mode 100644 index 0000000000000..ff2623b100bda --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.add_block.json @@ -0,0 +1,24 @@ +{ + "indices.add_block": { + "url_params": { + "timeout": "", + "master_timeout": "", + "ignore_unavailable": "__flag__", + "allow_no_indices": "__flag__", + "expand_wildcards": [ + "open", + "closed", + "hidden", + "none", + "all" + ] + }, + "methods": [ + "PUT" + ], + "patterns": [ + "{indices}/_block/{block}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/index-modules-blocks.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.create.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.create.json index 1970f88b30958..8227e38d3c6d9 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.create.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.create.json @@ -1,7 +1,6 @@ { "indices.create": { "url_params": { - "include_type_name": "__flag__", "wait_for_active_shards": "", "timeout": "", "master_timeout": "" diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.create_data_stream.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.create_data_stream.json new file mode 100644 index 0000000000000..832d5b8de1f47 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.create_data_stream.json @@ -0,0 +1,11 @@ +{ + "indices.create_data_stream": { + "methods": [ + "PUT" + ], + "patterns": [ + "_data_stream/{name}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/data-streams.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.data_streams_stats.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.data_streams_stats.json new file mode 100644 index 0000000000000..6a88c351ff700 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.data_streams_stats.json @@ -0,0 +1,12 @@ +{ + "indices.data_streams_stats": { + "methods": [ + "GET" + ], + "patterns": [ + "_data_stream/_stats", + "_data_stream/{name}/_stats" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/data-streams.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.delete_data_stream.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.delete_data_stream.json new file mode 100644 index 0000000000000..9b91e3deb3a08 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.delete_data_stream.json @@ -0,0 +1,20 @@ +{ + "indices.delete_data_stream": { + "url_params": { + "expand_wildcards": [ + "open", + "closed", + "hidden", + "none", + "all" + ] + }, + "methods": [ + "DELETE" + ], + "patterns": [ + "_data_stream/{name}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/data-streams.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.delete_index_template.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.delete_index_template.json new file mode 100644 index 0000000000000..ef3f836207f17 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.delete_index_template.json @@ -0,0 +1,15 @@ +{ + "indices.delete_index_template": { + "url_params": { + "timeout": "", + "master_timeout": "" + }, + "methods": [ + "DELETE" + ], + "patterns": [ + "_index_template/{name}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-templates.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.disk_usage.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.disk_usage.json new file mode 100644 index 0000000000000..fe7d378b2b168 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.disk_usage.json @@ -0,0 +1,24 @@ +{ + "indices.disk_usage": { + "url_params": { + "run_expensive_tasks": "__flag__", + "flush": "__flag__", + "ignore_unavailable": "__flag__", + "allow_no_indices": "__flag__", + "expand_wildcards": [ + "open", + "closed", + "hidden", + "none", + "all" + ] + }, + "methods": [ + "POST" + ], + "patterns": [ + "{indices}/_disk_usage" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-disk-usage.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.exists_index_template.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.exists_index_template.json new file mode 100644 index 0000000000000..97fa8cf55576f --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.exists_index_template.json @@ -0,0 +1,16 @@ +{ + "indices.exists_index_template": { + "url_params": { + "flat_settings": "__flag__", + "master_timeout": "", + "local": "__flag__" + }, + "methods": [ + "HEAD" + ], + "patterns": [ + "_index_template/{name}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-templates.html" + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/indices.freeze.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.freeze.json similarity index 75% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/indices.freeze.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.freeze.json index 06e8c606f59f7..77c765b90bcdc 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/indices.freeze.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.freeze.json @@ -8,17 +8,14 @@ "expand_wildcards": [ "open", "closed", + "hidden", "none", "all" ], "wait_for_active_shards": "" }, - "methods": [ - "POST" - ], - "patterns": [ - "{indices}/_freeze" - ], - "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/frozen.html" + "methods": [], + "patterns": [], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/freeze-index-api.html" } } diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get.json index 6df796ed6c4cf..99a654d023ba2 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get.json @@ -1,7 +1,6 @@ { "indices.get": { "url_params": { - "include_type_name": "__flag__", "local": "__flag__", "ignore_unavailable": "__flag__", "allow_no_indices": "__flag__", diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_data_stream.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_data_stream.json new file mode 100644 index 0000000000000..45199a60f337d --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_data_stream.json @@ -0,0 +1,21 @@ +{ + "indices.get_data_stream": { + "url_params": { + "expand_wildcards": [ + "open", + "closed", + "hidden", + "none", + "all" + ] + }, + "methods": [ + "GET" + ], + "patterns": [ + "_data_stream", + "_data_stream/{name}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/data-streams.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_field_mapping.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_field_mapping.json index c95e2efc73fab..c50919af0a2f3 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_field_mapping.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_field_mapping.json @@ -1,7 +1,6 @@ { "indices.get_field_mapping": { "url_params": { - "include_type_name": "__flag__", "include_defaults": "__flag__", "ignore_unavailable": "__flag__", "allow_no_indices": "__flag__", diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_index_template.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_index_template.json new file mode 100644 index 0000000000000..142b75f22c2a6 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_index_template.json @@ -0,0 +1,17 @@ +{ + "indices.get_index_template": { + "url_params": { + "flat_settings": "__flag__", + "master_timeout": "", + "local": "__flag__" + }, + "methods": [ + "GET" + ], + "patterns": [ + "_index_template", + "_index_template/{name}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-templates.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_mapping.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_mapping.json index 555137d0e2ee0..f484f0dca0669 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_mapping.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_mapping.json @@ -1,7 +1,6 @@ { "indices.get_mapping": { "url_params": { - "include_type_name": "__flag__", "ignore_unavailable": "__flag__", "allow_no_indices": "__flag__", "expand_wildcards": [ diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_template.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_template.json index d5f52ec76b374..f5902929c25cc 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_template.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_template.json @@ -1,7 +1,6 @@ { "indices.get_template": { "url_params": { - "include_type_name": "__flag__", "flat_settings": "__flag__", "master_timeout": "", "local": "__flag__" diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.migrate_to_data_stream.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.migrate_to_data_stream.json new file mode 100644 index 0000000000000..45cd033801e55 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.migrate_to_data_stream.json @@ -0,0 +1,11 @@ +{ + "indices.migrate_to_data_stream": { + "methods": [ + "POST" + ], + "patterns": [ + "_data_stream/_migrate/{name}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/data-streams.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.promote_data_stream.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.promote_data_stream.json new file mode 100644 index 0000000000000..a92b9a7a4a40d --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.promote_data_stream.json @@ -0,0 +1,11 @@ +{ + "indices.promote_data_stream": { + "methods": [ + "POST" + ], + "patterns": [ + "_data_stream/_promote/{name}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/data-streams.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.put_index_template.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.put_index_template.json new file mode 100644 index 0000000000000..0ce27c1d9d21e --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.put_index_template.json @@ -0,0 +1,17 @@ +{ + "indices.put_index_template": { + "url_params": { + "create": "__flag__", + "cause": "", + "master_timeout": "" + }, + "methods": [ + "PUT", + "POST" + ], + "patterns": [ + "_index_template/{name}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-templates.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.put_mapping.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.put_mapping.json index e36783c815e3f..5b8ce2b8b4350 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.put_mapping.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.put_mapping.json @@ -1,7 +1,6 @@ { "indices.put_mapping": { "url_params": { - "include_type_name": "__flag__", "timeout": "", "master_timeout": "", "ignore_unavailable": "__flag__", @@ -12,7 +11,8 @@ "hidden", "none", "all" - ] + ], + "write_index_only": "__flag__" }, "methods": [ "PUT", diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.put_template.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.put_template.json index 8fb31277da44e..537428f3c53c9 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.put_template.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.put_template.json @@ -1,11 +1,9 @@ { "indices.put_template": { "url_params": { - "include_type_name": "__flag__", "order": "", "create": "__flag__", - "master_timeout": "", - "flat_settings": "__flag__" + "master_timeout": "" }, "methods": [ "PUT", diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/indices.reload_search_analyzers.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.reload_search_analyzers.json similarity index 96% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/indices.reload_search_analyzers.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.reload_search_analyzers.json index 13ae3a0fd4d18..d4d1d8c52b349 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/indices.reload_search_analyzers.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.reload_search_analyzers.json @@ -6,6 +6,7 @@ "expand_wildcards": [ "open", "closed", + "hidden", "none", "all" ] diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.resolve_index.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.resolve_index.json new file mode 100644 index 0000000000000..574d07f1e4324 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.resolve_index.json @@ -0,0 +1,20 @@ +{ + "indices.resolve_index": { + "url_params": { + "expand_wildcards": [ + "open", + "closed", + "hidden", + "none", + "all" + ] + }, + "methods": [ + "GET" + ], + "patterns": [ + "_resolve/index/{name}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-resolve-index-api.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.rollover.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.rollover.json index 7fa76a687eb77..19e0f1f909ab8 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.rollover.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.rollover.json @@ -1,7 +1,6 @@ { "indices.rollover": { "url_params": { - "include_type_name": "__flag__", "timeout": "", "dry_run": "__flag__", "master_timeout": "", diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.shrink.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.shrink.json index 6fbdea0f1244b..31acc86a2fa56 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.shrink.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.shrink.json @@ -1,7 +1,6 @@ { "indices.shrink": { "url_params": { - "copy_settings": "__flag__", "timeout": "", "master_timeout": "", "wait_for_active_shards": "" diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.simulate_index_template.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.simulate_index_template.json new file mode 100644 index 0000000000000..e122bd844e9bc --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.simulate_index_template.json @@ -0,0 +1,16 @@ +{ + "indices.simulate_index_template": { + "url_params": { + "create": "__flag__", + "cause": "", + "master_timeout": "" + }, + "methods": [ + "POST" + ], + "patterns": [ + "_index_template/_simulate_index/{name}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-templates.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.simulate_template.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.simulate_template.json new file mode 100644 index 0000000000000..9e174799e6c07 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.simulate_template.json @@ -0,0 +1,17 @@ +{ + "indices.simulate_template": { + "url_params": { + "create": "__flag__", + "cause": "", + "master_timeout": "" + }, + "methods": [ + "POST" + ], + "patterns": [ + "_index_template/_simulate", + "_index_template/_simulate/{name}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-templates.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.split.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.split.json index 68f2e338cd201..1bfbaa078b796 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.split.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.split.json @@ -1,7 +1,6 @@ { "indices.split": { "url_params": { - "copy_settings": "__flag__", "timeout": "", "master_timeout": "", "wait_for_active_shards": "" diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.stats.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.stats.json index 1fa32265c91ee..9cdcbdf672d8c 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.stats.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.stats.json @@ -34,6 +34,7 @@ "url_components": { "metrics": [ "_all", + "bulk", "completion", "docs", "fielddata", @@ -47,7 +48,6 @@ "search", "segments", "store", - "suggest", "warmer" ], "indices": null diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/indices.unfreeze.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.unfreeze.json similarity index 75% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/indices.unfreeze.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.unfreeze.json index 186a671347240..3e87971cb66f5 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/indices.unfreeze.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.unfreeze.json @@ -8,17 +8,14 @@ "expand_wildcards": [ "open", "closed", + "hidden", "none", "all" ], "wait_for_active_shards": "" }, - "methods": [ - "POST" - ], - "patterns": [ - "{indices}/_unfreeze" - ], - "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/frozen.html" + "methods": [], + "patterns": [], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/unfreeze-index-api.html" } } diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/ingest.geo_ip_stats.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ingest.geo_ip_stats.json new file mode 100644 index 0000000000000..2993eca9f0af5 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ingest.geo_ip_stats.json @@ -0,0 +1,11 @@ +{ + "ingest.geo_ip_stats": { + "methods": [ + "GET" + ], + "patterns": [ + "_ingest/geoip/stats" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/geoip-stats-api.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/ingest.get_pipeline.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ingest.get_pipeline.json index cffcbb1261f90..df90b976444c1 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/ingest.get_pipeline.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ingest.get_pipeline.json @@ -1,6 +1,7 @@ { "ingest.get_pipeline": { "url_params": { + "summary": "__flag__", "master_timeout": "" }, "methods": [ diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/license.delete.json b/src/plugins/console/server/lib/spec_definitions/json/generated/license.delete.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/license.delete.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/license.delete.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/license.get.json b/src/plugins/console/server/lib/spec_definitions/json/generated/license.get.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/license.get.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/license.get.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/license.get_basic_status.json b/src/plugins/console/server/lib/spec_definitions/json/generated/license.get_basic_status.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/license.get_basic_status.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/license.get_basic_status.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/license.get_trial_status.json b/src/plugins/console/server/lib/spec_definitions/json/generated/license.get_trial_status.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/license.get_trial_status.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/license.get_trial_status.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/license.post.json b/src/plugins/console/server/lib/spec_definitions/json/generated/license.post.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/license.post.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/license.post.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/license.post_start_basic.json b/src/plugins/console/server/lib/spec_definitions/json/generated/license.post_start_basic.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/license.post_start_basic.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/license.post_start_basic.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/license.post_start_trial.json b/src/plugins/console/server/lib/spec_definitions/json/generated/license.post_start_trial.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/license.post_start_trial.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/license.post_start_trial.json diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/logstash.delete_pipeline.json b/src/plugins/console/server/lib/spec_definitions/json/generated/logstash.delete_pipeline.json new file mode 100644 index 0000000000000..9e2c32e287253 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/logstash.delete_pipeline.json @@ -0,0 +1,11 @@ +{ + "logstash.delete_pipeline": { + "methods": [ + "DELETE" + ], + "patterns": [ + "_logstash/pipeline/{id}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/logstash-api-delete-pipeline.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/logstash.get_pipeline.json b/src/plugins/console/server/lib/spec_definitions/json/generated/logstash.get_pipeline.json new file mode 100644 index 0000000000000..a6b3a69b53a60 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/logstash.get_pipeline.json @@ -0,0 +1,11 @@ +{ + "logstash.get_pipeline": { + "methods": [ + "GET" + ], + "patterns": [ + "_logstash/pipeline/{id}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/logstash-api-get-pipeline.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/logstash.put_pipeline.json b/src/plugins/console/server/lib/spec_definitions/json/generated/logstash.put_pipeline.json new file mode 100644 index 0000000000000..9640ced182898 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/logstash.put_pipeline.json @@ -0,0 +1,11 @@ +{ + "logstash.put_pipeline": { + "methods": [ + "PUT" + ], + "patterns": [ + "_logstash/pipeline/{id}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/logstash-api-put-pipeline.html" + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/migration.deprecations.json b/src/plugins/console/server/lib/spec_definitions/json/generated/migration.deprecations.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/migration.deprecations.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/migration.deprecations.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/migration.get_assistance.json b/src/plugins/console/server/lib/spec_definitions/json/generated/migration.get_assistance.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/migration.get_assistance.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/migration.get_assistance.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/migration.upgrade.json b/src/plugins/console/server/lib/spec_definitions/json/generated/migration.upgrade.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/migration.upgrade.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/migration.upgrade.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.close_job.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.close_job.json similarity index 90% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.close_job.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.close_job.json index b0f2c6489b30e..4fd0b7ccf98a7 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.close_job.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.close_job.json @@ -1,6 +1,7 @@ { "ml.close_job": { "url_params": { + "allow_no_match": "__flag__", "allow_no_jobs": "__flag__", "force": "__flag__", "timeout": "" diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_calendar.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_calendar.json similarity index 51% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_calendar.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_calendar.json index 97e5898072a08..7ccf7ef673070 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_calendar.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_calendar.json @@ -5,6 +5,7 @@ ], "patterns": [ "_ml/calendars/{calendar_id}" - ] + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-delete-calendar.html" } } diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_calendar_event.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_calendar_event.json similarity index 54% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_calendar_event.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_calendar_event.json index 85ea5add0b60b..7ee8ef376a4db 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_calendar_event.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_calendar_event.json @@ -5,6 +5,7 @@ ], "patterns": [ "_ml/calendars/{calendar_id}/events/{event_id}" - ] + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-delete-calendar-event.html" } } diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_calendar_job.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_calendar_job.json similarity index 54% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_calendar_job.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_calendar_job.json index 6665f52eeb90d..b97667900fe35 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_calendar_job.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_calendar_job.json @@ -5,6 +5,7 @@ ], "patterns": [ "_ml/calendars/{calendar_id}/jobs/{job_id}" - ] + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-delete-calendar-job.html" } } diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_data_frame_analytics.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_data_frame_analytics.json similarity index 85% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_data_frame_analytics.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_data_frame_analytics.json index 2e4593f339212..a46bfdf8318db 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_data_frame_analytics.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_data_frame_analytics.json @@ -1,7 +1,8 @@ { "ml.delete_data_frame_analytics": { "url_params": { - "force": "__flag__" + "force": "__flag__", + "timeout": "" }, "methods": [ "DELETE" diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_datafeed.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_datafeed.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_datafeed.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_datafeed.json diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_expired_data.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_expired_data.json new file mode 100644 index 0000000000000..1515821cb1628 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_expired_data.json @@ -0,0 +1,16 @@ +{ + "ml.delete_expired_data": { + "url_params": { + "requests_per_second": "", + "timeout": "" + }, + "methods": [ + "DELETE" + ], + "patterns": [ + "_ml/_delete_expired_data/{job_id}", + "_ml/_delete_expired_data" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-delete-expired-data.html" + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_filter.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_filter.json similarity index 51% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_filter.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_filter.json index 8210a2acd71d0..515986d44d77c 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_filter.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_filter.json @@ -5,6 +5,7 @@ ], "patterns": [ "_ml/filters/{filter_id}" - ] + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-delete-filter.html" } } diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_forecast.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_forecast.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_forecast.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_forecast.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_job.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_job.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_job.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_job.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_model_snapshot.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_model_snapshot.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_model_snapshot.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_model_snapshot.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_trained_model.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_trained_model.json similarity index 65% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_trained_model.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_trained_model.json index 343fa904c4216..75592c5964ed0 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_trained_model.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_trained_model.json @@ -4,8 +4,8 @@ "DELETE" ], "patterns": [ - "_ml/inference/{model_id}" + "_ml/trained_models/{model_id}" ], - "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/delete-inference.html" + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/delete-trained-models.html" } } diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_trained_model_alias.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_trained_model_alias.json new file mode 100644 index 0000000000000..cd27f21b1d6e4 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.delete_trained_model_alias.json @@ -0,0 +1,11 @@ +{ + "ml.delete_trained_model_alias": { + "methods": [ + "DELETE" + ], + "patterns": [ + "_ml/trained_models/{model_id}/model_aliases/{model_alias}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/delete-trained-models-aliases.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/ml.estimate_model_memory.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.estimate_model_memory.json new file mode 100644 index 0000000000000..94b149e542d43 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.estimate_model_memory.json @@ -0,0 +1,11 @@ +{ + "ml.estimate_model_memory": { + "methods": [ + "POST" + ], + "patterns": [ + "_ml/anomaly_detectors/_estimate_model_memory" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-apis.html" + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.evaluate_data_frame.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.evaluate_data_frame.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.evaluate_data_frame.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.evaluate_data_frame.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.explain_data_frame_analytics.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.explain_data_frame_analytics.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.explain_data_frame_analytics.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.explain_data_frame_analytics.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.find_file_structure.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.find_file_structure.json similarity index 88% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.find_file_structure.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.find_file_structure.json index 6e7163ae2b740..9ede28306e313 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.find_file_structure.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.find_file_structure.json @@ -25,8 +25,8 @@ "POST" ], "patterns": [ - "_ml/find_file_structure" + "_text_structure/find_structure" ], - "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-find-file-structure.html" + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/find-structure.html" } } diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.flush_job.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.flush_job.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.flush_job.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.flush_job.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.forecast.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.forecast.json similarity index 52% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.forecast.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.forecast.json index 3a8849aad3e4d..d2b53c11cb985 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.forecast.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.forecast.json @@ -2,13 +2,15 @@ "ml.forecast": { "url_params": { "duration": "", - "expires_in": "" + "expires_in": "", + "max_model_memory": "" }, "methods": [ "POST" ], "patterns": [ "_ml/anomaly_detectors/{job_id}/_forecast" - ] + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-forecast.html" } } diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_buckets.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_buckets.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_buckets.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_buckets.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_calendar_events.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_calendar_events.json similarity index 67% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_calendar_events.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_calendar_events.json index 8999af6320dfa..f8886067dd80e 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_calendar_events.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_calendar_events.json @@ -12,6 +12,7 @@ ], "patterns": [ "_ml/calendars/{calendar_id}/events" - ] + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-get-calendar-event.html" } } diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_calendars.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_calendars.json similarity index 65% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_calendars.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_calendars.json index 87f582b5c364d..575f2573d1ee9 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_calendars.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_calendars.json @@ -11,6 +11,7 @@ "patterns": [ "_ml/calendars", "_ml/calendars/{calendar_id}" - ] + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-get-calendar.html" } } diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_categories.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_categories.json similarity index 88% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_categories.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_categories.json index 64edb196bb366..1083760d7594f 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_categories.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_categories.json @@ -2,7 +2,8 @@ "ml.get_categories": { "url_params": { "from": 0, - "size": 0 + "size": 0, + "partition_field_value": "" }, "methods": [ "GET", diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_data_frame_analytics.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_data_frame_analytics.json similarity index 86% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_data_frame_analytics.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_data_frame_analytics.json index ecccec9c7e059..cccaa516110aa 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_data_frame_analytics.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_data_frame_analytics.json @@ -3,7 +3,8 @@ "url_params": { "allow_no_match": "__flag__", "from": 0, - "size": 0 + "size": 0, + "exclude_generated": "__flag__" }, "methods": [ "GET" diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_data_frame_analytics_stats.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_data_frame_analytics_stats.json similarity index 89% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_data_frame_analytics_stats.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_data_frame_analytics_stats.json index 3ae103f79f798..20fbb2981a28c 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_data_frame_analytics_stats.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_data_frame_analytics_stats.json @@ -3,7 +3,8 @@ "url_params": { "allow_no_match": "__flag__", "from": 0, - "size": 0 + "size": 0, + "verbose": "__flag__" }, "methods": [ "GET" diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_datafeed_stats.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_datafeed_stats.json similarity index 90% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_datafeed_stats.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_datafeed_stats.json index 2971b8a7f6c63..75ece20e87443 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_datafeed_stats.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_datafeed_stats.json @@ -1,6 +1,7 @@ { "ml.get_datafeed_stats": { "url_params": { + "allow_no_match": "__flag__", "allow_no_datafeeds": "__flag__" }, "methods": [ diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_datafeeds.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_datafeeds.json similarity index 71% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_datafeeds.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_datafeeds.json index deeb81d692739..ec557966a7271 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_datafeeds.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_datafeeds.json @@ -1,7 +1,9 @@ { "ml.get_datafeeds": { "url_params": { - "allow_no_datafeeds": "__flag__" + "allow_no_match": "__flag__", + "allow_no_datafeeds": "__flag__", + "exclude_generated": "__flag__" }, "methods": [ "GET" diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_filters.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_filters.json similarity index 63% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_filters.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_filters.json index 9b06e618a0b0c..df5747e143412 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_filters.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_filters.json @@ -10,6 +10,7 @@ "patterns": [ "_ml/filters", "_ml/filters/{filter_id}" - ] + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-get-filter.html" } } diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_influencers.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_influencers.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_influencers.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_influencers.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_job_stats.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_job_stats.json similarity index 90% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_job_stats.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_job_stats.json index 6173b3ebdc6d0..48dd9e52cd990 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_job_stats.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_job_stats.json @@ -1,6 +1,7 @@ { "ml.get_job_stats": { "url_params": { + "allow_no_match": "__flag__", "allow_no_jobs": "__flag__" }, "methods": [ diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_jobs.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_jobs.json similarity index 72% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_jobs.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_jobs.json index 2486684424670..b46c86559ea09 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_jobs.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_jobs.json @@ -1,7 +1,9 @@ { "ml.get_jobs": { "url_params": { - "allow_no_jobs": "__flag__" + "allow_no_match": "__flag__", + "allow_no_jobs": "__flag__", + "exclude_generated": "__flag__" }, "methods": [ "GET" diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_model_snapshots.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_model_snapshots.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_model_snapshots.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_model_snapshots.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_overall_buckets.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_overall_buckets.json similarity index 93% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_overall_buckets.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_overall_buckets.json index 3a88c9d8ab9c9..e374f6c5ef63f 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_overall_buckets.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_overall_buckets.json @@ -7,6 +7,7 @@ "exclude_interim": "__flag__", "start": "", "end": "", + "allow_no_match": "__flag__", "allow_no_jobs": "__flag__" }, "methods": [ diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_records.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_records.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_records.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_records.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_trained_models.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_trained_models.json similarity index 64% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_trained_models.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_trained_models.json index 76598ee015c6d..19f169fa98e86 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_trained_models.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_trained_models.json @@ -2,19 +2,21 @@ "ml.get_trained_models": { "url_params": { "allow_no_match": "__flag__", + "include": "", "include_model_definition": "__flag__", "decompress_definition": "__flag__", "from": 0, "size": 0, - "tags": [] + "tags": [], + "exclude_generated": "__flag__" }, "methods": [ "GET" ], "patterns": [ - "_ml/inference/{model_id}", - "_ml/inference" + "_ml/trained_models/{model_id}", + "_ml/trained_models" ], - "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/get-inference.html" + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/get-trained-models.html" } } diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_trained_models_stats.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_trained_models_stats.json similarity index 66% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_trained_models_stats.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_trained_models_stats.json index ab05e203b3980..44d06c78aef70 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.get_trained_models_stats.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.get_trained_models_stats.json @@ -9,9 +9,9 @@ "GET" ], "patterns": [ - "_ml/inference/{model_id}/_stats", - "_ml/inference/_stats" + "_ml/trained_models/{model_id}/_stats", + "_ml/trained_models/_stats" ], - "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/get-inference-stats.html" + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/get-trained-models-stats.html" } } diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/ml.infer_trained_model_deployment.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.infer_trained_model_deployment.json new file mode 100644 index 0000000000000..644942801d2f6 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.infer_trained_model_deployment.json @@ -0,0 +1,14 @@ +{ + "ml.infer_trained_model_deployment": { + "url_params": { + "timeout": "" + }, + "methods": [ + "POST" + ], + "patterns": [ + "_ml/trained_models/{model_id}/deployment/_infer" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-infer-trained-model-deployment.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/ml.info.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.info.json new file mode 100644 index 0000000000000..e09491dacf1fd --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.info.json @@ -0,0 +1,11 @@ +{ + "ml.info": { + "methods": [ + "GET" + ], + "patterns": [ + "_ml/info" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/get-ml-info.html" + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.open_job.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.open_job.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.open_job.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.open_job.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.post_calendar_events.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.post_calendar_events.json similarity index 52% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.post_calendar_events.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.post_calendar_events.json index 89ce2df63315b..9d0947c4704a1 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.post_calendar_events.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.post_calendar_events.json @@ -5,6 +5,7 @@ ], "patterns": [ "_ml/calendars/{calendar_id}/events" - ] + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-post-calendar-event.html" } } diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.post_data.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.post_data.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.post_data.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.post_data.json diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/ml.preview_data_frame_analytics.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.preview_data_frame_analytics.json new file mode 100644 index 0000000000000..bc3e0a4fd54b5 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.preview_data_frame_analytics.json @@ -0,0 +1,13 @@ +{ + "ml.preview_data_frame_analytics": { + "methods": [ + "GET", + "POST" + ], + "patterns": [ + "_ml/data_frame/analytics/_preview", + "_ml/data_frame/analytics/{id}/_preview" + ], + "documentation": "http://www.elastic.co/guide/en/elasticsearch/reference/current/preview-dfanalytics.html" + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.preview_datafeed.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.preview_datafeed.json similarity index 65% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.preview_datafeed.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.preview_datafeed.json index 6eb537804134b..29dbabd67d60b 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.preview_datafeed.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.preview_datafeed.json @@ -1,10 +1,12 @@ { "ml.preview_datafeed": { "methods": [ - "GET" + "GET", + "POST" ], "patterns": [ - "_ml/datafeeds/{datafeed_id}/_preview" + "_ml/datafeeds/{datafeed_id}/_preview", + "_ml/datafeeds/_preview" ], "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-preview-datafeed.html" } diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.put_calendar.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.put_calendar.json similarity index 51% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.put_calendar.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.put_calendar.json index 7452d1b4b9707..c60a9b60f4ca0 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.put_calendar.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.put_calendar.json @@ -5,6 +5,7 @@ ], "patterns": [ "_ml/calendars/{calendar_id}" - ] + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-put-calendar.html" } } diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.put_calendar_job.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.put_calendar_job.json similarity index 53% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.put_calendar_job.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.put_calendar_job.json index 08dc3d77b5f25..5a3ac8e0b3243 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.put_calendar_job.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.put_calendar_job.json @@ -5,6 +5,7 @@ ], "patterns": [ "_ml/calendars/{calendar_id}/jobs/{job_id}" - ] + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-put-calendar-job.html" } } diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.put_data_frame_analytics.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.put_data_frame_analytics.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.put_data_frame_analytics.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.put_data_frame_analytics.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.put_datafeed.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.put_datafeed.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.put_datafeed.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.put_datafeed.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.put_job.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.put_filter.json similarity index 58% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.put_job.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.put_filter.json index 7a48994bd1a6c..ad9caa9417da9 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.put_job.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.put_filter.json @@ -1,11 +1,11 @@ { - "ml.put_job": { + "ml.put_filter": { "methods": [ "PUT" ], "patterns": [ - "_ml/anomaly_detectors/{job_id}" + "_ml/filters/{filter_id}" ], - "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-put-job.html" + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-put-filter.html" } } diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/ml.put_job.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.put_job.json new file mode 100644 index 0000000000000..a55ee4ab21a63 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.put_job.json @@ -0,0 +1,23 @@ +{ + "ml.put_job": { + "url_params": { + "ignore_unavailable": "__flag__", + "allow_no_indices": "__flag__", + "ignore_throttled": "__flag__", + "expand_wildcards": [ + "open", + "closed", + "hidden", + "none", + "all" + ] + }, + "methods": [ + "PUT" + ], + "patterns": [ + "_ml/anomaly_detectors/{job_id}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-put-job.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/ml.put_trained_model.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.put_trained_model.json new file mode 100644 index 0000000000000..9b95f6891d474 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.put_trained_model.json @@ -0,0 +1,11 @@ +{ + "ml.put_trained_model": { + "methods": [ + "PUT" + ], + "patterns": [ + "_ml/trained_models/{model_id}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/put-trained-models.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/ml.put_trained_model_alias.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.put_trained_model_alias.json new file mode 100644 index 0000000000000..a83d54220b31a --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.put_trained_model_alias.json @@ -0,0 +1,14 @@ +{ + "ml.put_trained_model_alias": { + "url_params": { + "reassign": "__flag__" + }, + "methods": [ + "PUT" + ], + "patterns": [ + "_ml/trained_models/{model_id}/model_aliases/{model_alias}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/put-trained-models-aliases.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/ml.reset_job.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.reset_job.json new file mode 100644 index 0000000000000..79441afbdebf7 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.reset_job.json @@ -0,0 +1,14 @@ +{ + "ml.reset_job": { + "url_params": { + "wait_for_completion": "__flag__" + }, + "methods": [ + "POST" + ], + "patterns": [ + "_ml/anomaly_detectors/{job_id}/_reset" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-reset-job.html" + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.revert_model_snapshot.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.revert_model_snapshot.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.revert_model_snapshot.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.revert_model_snapshot.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.set_upgrade_mode.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.set_upgrade_mode.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.set_upgrade_mode.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.set_upgrade_mode.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.start_data_frame_analytics.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.start_data_frame_analytics.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.start_data_frame_analytics.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.start_data_frame_analytics.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.start_datafeed.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.start_datafeed.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.start_datafeed.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.start_datafeed.json diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/ml.start_trained_model_deployment.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.start_trained_model_deployment.json new file mode 100644 index 0000000000000..49e246c90d8e1 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.start_trained_model_deployment.json @@ -0,0 +1,14 @@ +{ + "ml.start_trained_model_deployment": { + "url_params": { + "timeout": "" + }, + "methods": [ + "POST" + ], + "patterns": [ + "_ml/trained_models/{model_id}/deployment/_start" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-start-trained-model-deployment.html" + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.stop_data_frame_analytics.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.stop_data_frame_analytics.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.stop_data_frame_analytics.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.stop_data_frame_analytics.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.stop_datafeed.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.stop_datafeed.json similarity index 90% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.stop_datafeed.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.stop_datafeed.json index d6769ed58148f..b52f1e22bddf8 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.stop_datafeed.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.stop_datafeed.json @@ -1,6 +1,7 @@ { "ml.stop_datafeed": { "url_params": { + "allow_no_match": "__flag__", "allow_no_datafeeds": "__flag__", "force": "__flag__", "timeout": "" diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/ml.stop_trained_model_deployment.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.stop_trained_model_deployment.json new file mode 100644 index 0000000000000..94b34f597fc3b --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.stop_trained_model_deployment.json @@ -0,0 +1,11 @@ +{ + "ml.stop_trained_model_deployment": { + "methods": [ + "POST" + ], + "patterns": [ + "_ml/trained_models/{model_id}/deployment/_stop" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/stop-trained-model-deployment.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/ml.update_data_frame_analytics.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.update_data_frame_analytics.json new file mode 100644 index 0000000000000..e3134770742cb --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.update_data_frame_analytics.json @@ -0,0 +1,11 @@ +{ + "ml.update_data_frame_analytics": { + "methods": [ + "POST" + ], + "patterns": [ + "_ml/data_frame/analytics/{id}/_update" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/update-dfanalytics.html" + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.update_datafeed.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.update_datafeed.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.update_datafeed.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.update_datafeed.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.update_filter.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.update_filter.json similarity index 52% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.update_filter.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.update_filter.json index 3f48013c2be1c..16b6aedb404d3 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.update_filter.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.update_filter.json @@ -5,6 +5,7 @@ ], "patterns": [ "_ml/filters/{filter_id}/_update" - ] + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-update-filter.html" } } diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.update_job.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.update_job.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.update_job.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.update_job.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.update_model_snapshot.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.update_model_snapshot.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.update_model_snapshot.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.update_model_snapshot.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.upgrade.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.upgrade.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.upgrade.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.upgrade.json diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/ml.upgrade_job_snapshot.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.upgrade_job_snapshot.json new file mode 100644 index 0000000000000..6818eab2bb084 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.upgrade_job_snapshot.json @@ -0,0 +1,15 @@ +{ + "ml.upgrade_job_snapshot": { + "url_params": { + "timeout": "", + "wait_for_completion": "__flag__" + }, + "methods": [ + "POST" + ], + "patterns": [ + "_ml/anomaly_detectors/{job_id}/model_snapshots/{snapshot_id}/_upgrade" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-upgrade-job-model-snapshot.html" + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.validate.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.validate.json similarity index 54% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.validate.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.validate.json index 298c7cf6dce26..da07f1a2fa203 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.validate.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.validate.json @@ -5,6 +5,7 @@ ], "patterns": [ "_ml/anomaly_detectors/_validate" - ] + ], + "documentation": "https://www.elastic.co/guide/en/machine-learning/current/ml-jobs.html" } } diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.validate_detector.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.validate_detector.json similarity index 58% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.validate_detector.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ml.validate_detector.json index 5e6d7daeea772..73527bd28c5d8 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.validate_detector.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.validate_detector.json @@ -5,6 +5,7 @@ ], "patterns": [ "_ml/anomaly_detectors/_validate/detector" - ] + ], + "documentation": "https://www.elastic.co/guide/en/machine-learning/current/ml-jobs.html" } } diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/monitoring.bulk.json b/src/plugins/console/server/lib/spec_definitions/json/generated/monitoring.bulk.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/monitoring.bulk.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/monitoring.bulk.json diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/msearch.json b/src/plugins/console/server/lib/spec_definitions/json/generated/msearch.json index ecb71e9ba23c0..d460bf2b1add5 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/msearch.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/msearch.json @@ -3,9 +3,7 @@ "url_params": { "search_type": [ "query_then_fetch", - "query_and_fetch", - "dfs_query_then_fetch", - "dfs_query_and_fetch" + "dfs_query_then_fetch" ], "max_concurrent_searches": "", "typed_keys": "__flag__", diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/msearch_template.json b/src/plugins/console/server/lib/spec_definitions/json/generated/msearch_template.json index 0b0ca087b1819..cc7cfbd6e82b6 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/msearch_template.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/msearch_template.json @@ -3,9 +3,7 @@ "url_params": { "search_type": [ "query_then_fetch", - "query_and_fetch", - "dfs_query_then_fetch", - "dfs_query_and_fetch" + "dfs_query_then_fetch" ], "typed_keys": "__flag__", "max_concurrent_searches": "", diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/mtermvectors.json b/src/plugins/console/server/lib/spec_definitions/json/generated/mtermvectors.json index 72a134eca4d2e..20c3daca849c7 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/mtermvectors.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/mtermvectors.json @@ -15,8 +15,7 @@ "version_type": [ "internal", "external", - "external_gte", - "force" + "external_gte" ] }, "methods": [ diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/nodes.stats.json b/src/plugins/console/server/lib/spec_definitions/json/generated/nodes.stats.json index e194a6d42c031..cfbcf69b39b7a 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/nodes.stats.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/nodes.stats.json @@ -12,7 +12,8 @@ ], "types": [], "timeout": "", - "include_segment_file_sizes": "__flag__" + "include_segment_file_sizes": "__flag__", + "include_unloaded_segments": "__flag__" }, "methods": [ "GET" @@ -33,6 +34,7 @@ "discovery", "fs", "http", + "indexing_pressure", "indices", "jvm", "os", @@ -42,6 +44,7 @@ ], "index_metric": [ "_all", + "bulk", "completion", "docs", "fielddata", @@ -55,7 +58,6 @@ "search", "segments", "store", - "suggest", "warmer" ] }, diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/open_point_in_time.json b/src/plugins/console/server/lib/spec_definitions/json/generated/open_point_in_time.json new file mode 100644 index 0000000000000..9ce78acbc1a2a --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/open_point_in_time.json @@ -0,0 +1,25 @@ +{ + "open_point_in_time": { + "url_params": { + "preference": "random", + "routing": "", + "ignore_unavailable": "__flag__", + "expand_wildcards": [ + "open", + "closed", + "hidden", + "none", + "all" + ], + "keep_alive": "" + }, + "methods": [ + "POST" + ], + "patterns": [ + "_pit", + "{indices}/_pit" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/point-in-time-api.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/render_search_template.json b/src/plugins/console/server/lib/spec_definitions/json/generated/render_search_template.json index e206140f9d16a..84377591e270b 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/render_search_template.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/render_search_template.json @@ -8,6 +8,6 @@ "_render/template", "_render/template/{id}" ], - "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/search-template.html#_validating_templates" + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/render-search-template-api.html" } } diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/rollup.delete_job.json b/src/plugins/console/server/lib/spec_definitions/json/generated/rollup.delete_job.json new file mode 100644 index 0000000000000..4f7b37006c3b6 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/rollup.delete_job.json @@ -0,0 +1,11 @@ +{ + "rollup.delete_job": { + "methods": [ + "DELETE" + ], + "patterns": [ + "_rollup/job/{id}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/rollup-delete-job.html" + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.get_jobs.json b/src/plugins/console/server/lib/spec_definitions/json/generated/rollup.get_jobs.json similarity index 53% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.get_jobs.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/rollup.get_jobs.json index 49524b394167c..70ce98063d57c 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.get_jobs.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/rollup.get_jobs.json @@ -6,6 +6,7 @@ "patterns": [ "_rollup/job/{id}", "_rollup/job/" - ] + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/rollup-get-job.html" } } diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.get_rollup_caps.json b/src/plugins/console/server/lib/spec_definitions/json/generated/rollup.get_rollup_caps.json similarity index 53% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.get_rollup_caps.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/rollup.get_rollup_caps.json index 8a0ef67a0ad4e..9d39f5a4e2ba8 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.get_rollup_caps.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/rollup.get_rollup_caps.json @@ -6,6 +6,7 @@ "patterns": [ "_rollup/data/{id}", "_rollup/data/" - ] + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/rollup-get-rollup-caps.html" } } diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.get_rollup_index_caps.json b/src/plugins/console/server/lib/spec_definitions/json/generated/rollup.get_rollup_index_caps.json similarity index 50% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.get_rollup_index_caps.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/rollup.get_rollup_index_caps.json index 325115553c294..f7ae6d14f675c 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.get_rollup_index_caps.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/rollup.get_rollup_index_caps.json @@ -5,6 +5,7 @@ ], "patterns": [ "{indices}/_rollup/data" - ] + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/rollup-get-rollup-index-caps.html" } } diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/rollup.put_job.json b/src/plugins/console/server/lib/spec_definitions/json/generated/rollup.put_job.json new file mode 100644 index 0000000000000..7970008f8c8f4 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/rollup.put_job.json @@ -0,0 +1,11 @@ +{ + "rollup.put_job": { + "methods": [ + "PUT" + ], + "patterns": [ + "_rollup/job/{id}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/rollup-put-job.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/rollup.rollup.json b/src/plugins/console/server/lib/spec_definitions/json/generated/rollup.rollup.json new file mode 100644 index 0000000000000..c3cc629abac80 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/rollup.rollup.json @@ -0,0 +1,11 @@ +{ + "rollup.rollup": { + "methods": [ + "POST" + ], + "patterns": [ + "{indices}/_rollup/{rollup_index}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/xpack-rollup.html" + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.rollup_search.json b/src/plugins/console/server/lib/spec_definitions/json/generated/rollup.rollup_search.json similarity index 68% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.rollup_search.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/rollup.rollup_search.json index a1771126a71b4..431e5e532a323 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.rollup_search.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/rollup.rollup_search.json @@ -10,6 +10,7 @@ ], "patterns": [ "{indices}/_rollup_search" - ] + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/rollup-search.html" } } diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.start_job.json b/src/plugins/console/server/lib/spec_definitions/json/generated/rollup.start_job.json similarity index 50% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.start_job.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/rollup.start_job.json index 9714275072b07..39ced395166c7 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.start_job.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/rollup.start_job.json @@ -5,6 +5,7 @@ ], "patterns": [ "_rollup/job/{id}/_start" - ] + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/rollup-start-job.html" } } diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.stop_job.json b/src/plugins/console/server/lib/spec_definitions/json/generated/rollup.stop_job.json similarity index 64% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.stop_job.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/rollup.stop_job.json index 70931030b4402..a1290cd388a09 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.stop_job.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/rollup.stop_job.json @@ -9,6 +9,7 @@ ], "patterns": [ "_rollup/job/{id}/_stop" - ] + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/rollup-stop-job.html" } } diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/search.json b/src/plugins/console/server/lib/spec_definitions/json/generated/search.json index 78b969d3ed8f2..f5e3a0041c7a6 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/search.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/search.json @@ -49,7 +49,7 @@ "suggest_text": "", "timeout": "", "track_scores": "__flag__", - "track_total_hits": "__flag__", + "track_total_hits": "", "allow_partial_search_results": "__flag__", "typed_keys": "__flag__", "version": "__flag__", @@ -58,7 +58,8 @@ "batched_reduce_size": "", "max_concurrent_shard_requests": "", "pre_filter_shard_size": "", - "rest_total_hits_as_int": "__flag__" + "rest_total_hits_as_int": "__flag__", + "min_compatible_shard_node": "" }, "methods": [ "GET", diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/search_template.json b/src/plugins/console/server/lib/spec_definitions/json/generated/search_template.json index 748326522e5c2..6d7b3aa34c107 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/search_template.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/search_template.json @@ -16,9 +16,7 @@ "scroll": "", "search_type": [ "query_then_fetch", - "query_and_fetch", - "dfs_query_then_fetch", - "dfs_query_and_fetch" + "dfs_query_then_fetch" ], "explain": "__flag__", "profile": "__flag__", diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/searchable_snapshots.cache_stats.json b/src/plugins/console/server/lib/spec_definitions/json/generated/searchable_snapshots.cache_stats.json new file mode 100644 index 0000000000000..5a53319c164f8 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/searchable_snapshots.cache_stats.json @@ -0,0 +1,15 @@ +{ + "searchable_snapshots.cache_stats": { + "url_params": { + "master_timeout": "" + }, + "methods": [ + "GET" + ], + "patterns": [ + "_searchable_snapshots/cache/stats", + "_searchable_snapshots/{nodes}/cache/stats" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/searchable-snapshots-api-cache-stats.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/searchable_snapshots.clear_cache.json b/src/plugins/console/server/lib/spec_definitions/json/generated/searchable_snapshots.clear_cache.json new file mode 100644 index 0000000000000..428991f758cdb --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/searchable_snapshots.clear_cache.json @@ -0,0 +1,23 @@ +{ + "searchable_snapshots.clear_cache": { + "url_params": { + "ignore_unavailable": "__flag__", + "allow_no_indices": "__flag__", + "expand_wildcards": [ + "open", + "closed", + "none", + "all" + ], + "index": [] + }, + "methods": [ + "POST" + ], + "patterns": [ + "_searchable_snapshots/cache/clear", + "{indices}/_searchable_snapshots/cache/clear" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/searchable-snapshots-apis.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/searchable_snapshots.mount.json b/src/plugins/console/server/lib/spec_definitions/json/generated/searchable_snapshots.mount.json new file mode 100644 index 0000000000000..6556c466120da --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/searchable_snapshots.mount.json @@ -0,0 +1,16 @@ +{ + "searchable_snapshots.mount": { + "url_params": { + "master_timeout": "", + "wait_for_completion": "__flag__", + "storage": "" + }, + "methods": [ + "POST" + ], + "patterns": [ + "_snapshot/{repository}/{snapshot}/_mount" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/searchable-snapshots-api-mount-snapshot.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/searchable_snapshots.stats.json b/src/plugins/console/server/lib/spec_definitions/json/generated/searchable_snapshots.stats.json new file mode 100644 index 0000000000000..ccf7522634a0e --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/searchable_snapshots.stats.json @@ -0,0 +1,19 @@ +{ + "searchable_snapshots.stats": { + "url_params": { + "level": [ + "cluster", + "indices", + "shards" + ] + }, + "methods": [ + "GET" + ], + "patterns": [ + "_searchable_snapshots/stats", + "{indices}/_searchable_snapshots/stats" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/searchable-snapshots-api-stats.html" + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.authenticate.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.authenticate.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.authenticate.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/security.authenticate.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.change_password.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.change_password.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.change_password.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/security.change_password.json diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/security.clear_api_key_cache.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.clear_api_key_cache.json new file mode 100644 index 0000000000000..0046dab4074e6 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/security.clear_api_key_cache.json @@ -0,0 +1,11 @@ +{ + "security.clear_api_key_cache": { + "methods": [ + "POST" + ], + "patterns": [ + "_security/api_key/{ids}/_clear_cache" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-clear-api-key-cache.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/security.clear_cached_privileges.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.clear_cached_privileges.json new file mode 100644 index 0000000000000..aaab692cefc35 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/security.clear_cached_privileges.json @@ -0,0 +1,11 @@ +{ + "security.clear_cached_privileges": { + "methods": [ + "POST" + ], + "patterns": [ + "_security/privilege/{application}/_clear_cache" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-clear-privilege-cache.html" + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.clear_cached_realms.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.clear_cached_realms.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.clear_cached_realms.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/security.clear_cached_realms.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.clear_cached_roles.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.clear_cached_roles.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.clear_cached_roles.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/security.clear_cached_roles.json diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/security.clear_cached_service_tokens.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.clear_cached_service_tokens.json new file mode 100644 index 0000000000000..2fc747518fbc3 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/security.clear_cached_service_tokens.json @@ -0,0 +1,11 @@ +{ + "security.clear_cached_service_tokens": { + "methods": [ + "POST" + ], + "patterns": [ + "_security/service/{namespace}/{service}/credential/token/{name}/_clear_cache" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-clear-service-token-caches.html" + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.create_api_key.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.create_api_key.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.create_api_key.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/security.create_api_key.json diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/security.create_service_token.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.create_service_token.json new file mode 100644 index 0000000000000..71c6ccedddc74 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/security.create_service_token.json @@ -0,0 +1,20 @@ +{ + "security.create_service_token": { + "url_params": { + "refresh": [ + "true", + "false", + "wait_for" + ] + }, + "methods": [ + "PUT", + "POST" + ], + "patterns": [ + "_security/service/{namespace}/{service}/credential/token/{name}", + "_security/service/{namespace}/{service}/credential/token" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-create-service-token.html" + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.delete_privileges.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.delete_privileges.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.delete_privileges.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/security.delete_privileges.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.delete_role.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.delete_role.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.delete_role.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/security.delete_role.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.delete_role_mapping.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.delete_role_mapping.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.delete_role_mapping.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/security.delete_role_mapping.json diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/security.delete_service_token.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.delete_service_token.json new file mode 100644 index 0000000000000..bfdfeb79cb27e --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/security.delete_service_token.json @@ -0,0 +1,18 @@ +{ + "security.delete_service_token": { + "url_params": { + "refresh": [ + "true", + "false", + "wait_for" + ] + }, + "methods": [ + "DELETE" + ], + "patterns": [ + "_security/service/{namespace}/{service}/credential/token/{name}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-delete-service-token.html" + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.delete_user.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.delete_user.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.delete_user.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/security.delete_user.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.disable_user.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.disable_user.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.disable_user.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/security.disable_user.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.enable_user.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.enable_user.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.enable_user.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/security.enable_user.json diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/security.enroll_kibana.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.enroll_kibana.json new file mode 100644 index 0000000000000..630adedd03ad7 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/security.enroll_kibana.json @@ -0,0 +1,11 @@ +{ + "security.enroll_kibana": { + "methods": [ + "GET" + ], + "patterns": [ + "_security/enroll/kibana" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/security-api-enroll-kibana.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/security.enroll_node.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.enroll_node.json new file mode 100644 index 0000000000000..d9cb7da17ed6e --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/security.enroll_node.json @@ -0,0 +1,11 @@ +{ + "security.enroll_node": { + "methods": [ + "GET" + ], + "patterns": [ + "_security/enroll/node" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/security-api-node-enrollment.html" + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.get_api_key.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.get_api_key.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.get_api_key.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/security.get_api_key.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.get_builtin_privileges.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.get_builtin_privileges.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.get_builtin_privileges.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/security.get_builtin_privileges.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.get_privileges.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.get_privileges.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.get_privileges.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/security.get_privileges.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.get_role.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.get_role.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.get_role.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/security.get_role.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.get_role_mapping.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.get_role_mapping.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.get_role_mapping.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/security.get_role_mapping.json diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/security.get_service_accounts.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.get_service_accounts.json new file mode 100644 index 0000000000000..41afc1de8df5c --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/security.get_service_accounts.json @@ -0,0 +1,13 @@ +{ + "security.get_service_accounts": { + "methods": [ + "GET" + ], + "patterns": [ + "_security/service/{namespace}/{service}", + "_security/service/{namespace}", + "_security/service" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-get-service-accounts.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/security.get_service_credentials.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.get_service_credentials.json new file mode 100644 index 0000000000000..b98029aaae367 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/security.get_service_credentials.json @@ -0,0 +1,11 @@ +{ + "security.get_service_credentials": { + "methods": [ + "GET" + ], + "patterns": [ + "_security/service/{namespace}/{service}/credential" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-get-service-credentials.html" + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.get_token.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.get_token.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.get_token.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/security.get_token.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.get_user.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.get_user.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.get_user.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/security.get_user.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.get_user_privileges.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.get_user_privileges.json similarity index 76% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.get_user_privileges.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/security.get_user_privileges.json index 63bf8d6969344..1b939d2d9a29e 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.get_user_privileges.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/security.get_user_privileges.json @@ -6,6 +6,6 @@ "patterns": [ "_security/user/_privileges" ], - "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-get-privileges.html" + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-get-user-privileges.html" } } diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/security.grant_api_key.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.grant_api_key.json new file mode 100644 index 0000000000000..4d1a56e4fb0bb --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/security.grant_api_key.json @@ -0,0 +1,18 @@ +{ + "security.grant_api_key": { + "url_params": { + "refresh": [ + "true", + "false", + "wait_for" + ] + }, + "methods": [ + "POST" + ], + "patterns": [ + "_security/api_key/grant" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-grant-api-key.html" + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.has_privileges.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.has_privileges.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.has_privileges.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/security.has_privileges.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.invalidate_api_key.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.invalidate_api_key.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.invalidate_api_key.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/security.invalidate_api_key.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.invalidate_token.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.invalidate_token.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.invalidate_token.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/security.invalidate_token.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.put_privileges.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.put_privileges.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.put_privileges.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/security.put_privileges.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.put_role.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.put_role.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.put_role.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/security.put_role.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.put_role_mapping.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.put_role_mapping.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.put_role_mapping.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/security.put_role_mapping.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.put_user.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.put_user.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/security.put_user.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/security.put_user.json diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/security.saml_authenticate.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.saml_authenticate.json new file mode 100644 index 0000000000000..5ed30f2a4ef83 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/security.saml_authenticate.json @@ -0,0 +1,11 @@ +{ + "security.saml_authenticate": { + "methods": [ + "POST" + ], + "patterns": [ + "_security/saml/authenticate" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-saml-authenticate.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/security.saml_complete_logout.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.saml_complete_logout.json new file mode 100644 index 0000000000000..7ecc4bdbc8784 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/security.saml_complete_logout.json @@ -0,0 +1,11 @@ +{ + "security.saml_complete_logout": { + "methods": [ + "POST" + ], + "patterns": [ + "_security/saml/complete_logout" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-saml-complete-logout.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/security.saml_invalidate.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.saml_invalidate.json new file mode 100644 index 0000000000000..cea5236c474f4 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/security.saml_invalidate.json @@ -0,0 +1,11 @@ +{ + "security.saml_invalidate": { + "methods": [ + "POST" + ], + "patterns": [ + "_security/saml/invalidate" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-saml-invalidate.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/security.saml_logout.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.saml_logout.json new file mode 100644 index 0000000000000..4300c948199b3 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/security.saml_logout.json @@ -0,0 +1,11 @@ +{ + "security.saml_logout": { + "methods": [ + "POST" + ], + "patterns": [ + "_security/saml/logout" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-saml-logout.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/security.saml_prepare_authentication.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.saml_prepare_authentication.json new file mode 100644 index 0000000000000..fa4303ccfedae --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/security.saml_prepare_authentication.json @@ -0,0 +1,11 @@ +{ + "security.saml_prepare_authentication": { + "methods": [ + "POST" + ], + "patterns": [ + "_security/saml/prepare" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-saml-prepare-authentication.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/security.saml_service_provider_metadata.json b/src/plugins/console/server/lib/spec_definitions/json/generated/security.saml_service_provider_metadata.json new file mode 100644 index 0000000000000..a77953309c9ce --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/security.saml_service_provider_metadata.json @@ -0,0 +1,11 @@ +{ + "security.saml_service_provider_metadata": { + "methods": [ + "GET" + ], + "patterns": [ + "_security/saml/metadata/{realm_name}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-saml-sp-metadata.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/shutdown.delete_node.json b/src/plugins/console/server/lib/spec_definitions/json/generated/shutdown.delete_node.json new file mode 100644 index 0000000000000..18605b2afc549 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/shutdown.delete_node.json @@ -0,0 +1,11 @@ +{ + "shutdown.delete_node": { + "methods": [ + "DELETE" + ], + "patterns": [ + "_nodes/{nodes}/shutdown" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/shutdown.get_node.json b/src/plugins/console/server/lib/spec_definitions/json/generated/shutdown.get_node.json new file mode 100644 index 0000000000000..0b9c9139b57e3 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/shutdown.get_node.json @@ -0,0 +1,12 @@ +{ + "shutdown.get_node": { + "methods": [ + "GET" + ], + "patterns": [ + "_nodes/shutdown", + "_nodes/{nodes}/shutdown" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/shutdown.put_node.json b/src/plugins/console/server/lib/spec_definitions/json/generated/shutdown.put_node.json new file mode 100644 index 0000000000000..aee49831305f1 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/shutdown.put_node.json @@ -0,0 +1,11 @@ +{ + "shutdown.put_node": { + "methods": [ + "PUT" + ], + "patterns": [ + "_nodes/{nodes}/shutdown" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current" + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/slm.delete_lifecycle.json b/src/plugins/console/server/lib/spec_definitions/json/generated/slm.delete_lifecycle.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/slm.delete_lifecycle.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/slm.delete_lifecycle.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/slm.execute_lifecycle.json b/src/plugins/console/server/lib/spec_definitions/json/generated/slm.execute_lifecycle.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/slm.execute_lifecycle.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/slm.execute_lifecycle.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/slm.execute_retention.json b/src/plugins/console/server/lib/spec_definitions/json/generated/slm.execute_retention.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/slm.execute_retention.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/slm.execute_retention.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/slm.get_lifecycle.json b/src/plugins/console/server/lib/spec_definitions/json/generated/slm.get_lifecycle.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/slm.get_lifecycle.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/slm.get_lifecycle.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/slm.get_stats.json b/src/plugins/console/server/lib/spec_definitions/json/generated/slm.get_stats.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/slm.get_stats.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/slm.get_stats.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/slm.get_status.json b/src/plugins/console/server/lib/spec_definitions/json/generated/slm.get_status.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/slm.get_status.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/slm.get_status.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/slm.put_lifecycle.json b/src/plugins/console/server/lib/spec_definitions/json/generated/slm.put_lifecycle.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/slm.put_lifecycle.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/slm.put_lifecycle.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/slm.start.json b/src/plugins/console/server/lib/spec_definitions/json/generated/slm.start.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/slm.start.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/slm.start.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/slm.stop.json b/src/plugins/console/server/lib/spec_definitions/json/generated/slm.stop.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/slm.stop.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/slm.stop.json diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.cleanup_repository.json b/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.cleanup_repository.json index eed3e597dc070..cd29d6c300dc6 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.cleanup_repository.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.cleanup_repository.json @@ -10,6 +10,6 @@ "patterns": [ "_snapshot/{repository}/_cleanup" ], - "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/modules-snapshots.html" + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/clean-up-snapshot-repo-api.html" } } diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.clone.json b/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.clone.json new file mode 100644 index 0000000000000..ef8eedf78ac91 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.clone.json @@ -0,0 +1,14 @@ +{ + "snapshot.clone": { + "url_params": { + "master_timeout": "" + }, + "methods": [ + "PUT" + ], + "patterns": [ + "_snapshot/{repository}/{snapshot}/_clone/{target_snapshot}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/modules-snapshots.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.get.json b/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.get.json index b37141728f6b7..8edb85f416b48 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.get.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.get.json @@ -3,7 +3,12 @@ "url_params": { "master_timeout": "", "ignore_unavailable": "__flag__", - "verbose": "__flag__" + "index_details": "__flag__", + "verbose": "__flag__", + "sort": ["start_time", "duration", "name", "index_count"], + "size": "", + "order": "", + "after": "" }, "methods": [ "GET" diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.repository_analyze.json b/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.repository_analyze.json new file mode 100644 index 0000000000000..cf591a66ad2fd --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.repository_analyze.json @@ -0,0 +1,24 @@ +{ + "snapshot.repository_analyze": { + "url_params": { + "blob_count": "", + "concurrency": "", + "read_node_count": "", + "early_read_node_count": "", + "seed": "", + "rare_action_probability": "", + "max_blob_size": "", + "max_total_data_size": "", + "timeout": "", + "detailed": "__flag__", + "rarely_abort_writes": "__flag__" + }, + "methods": [ + "POST" + ], + "patterns": [ + "_snapshot/{repository}/_analyze" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/modules-snapshots.html" + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/sql.clear_cursor.json b/src/plugins/console/server/lib/spec_definitions/json/generated/sql.clear_cursor.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/sql.clear_cursor.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/sql.clear_cursor.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/sql.query.json b/src/plugins/console/server/lib/spec_definitions/json/generated/sql.query.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/sql.query.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/sql.query.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/sql.translate.json b/src/plugins/console/server/lib/spec_definitions/json/generated/sql.translate.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/sql.translate.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/sql.translate.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ssl.certificates.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ssl.certificates.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ssl.certificates.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ssl.certificates.json diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/tasks.cancel.json b/src/plugins/console/server/lib/spec_definitions/json/generated/tasks.cancel.json index 7a84c6acb53a7..2583876b93625 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/tasks.cancel.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/tasks.cancel.json @@ -3,7 +3,8 @@ "url_params": { "nodes": [], "actions": [], - "parent_task_id": "" + "parent_task_id": "", + "wait_for_completion": "__flag__" }, "methods": [ "POST" diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/terms_enum.json b/src/plugins/console/server/lib/spec_definitions/json/generated/terms_enum.json new file mode 100644 index 0000000000000..c1cb06b8a195a --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/terms_enum.json @@ -0,0 +1,12 @@ +{ + "terms_enum": { + "methods": [ + "GET", + "POST" + ], + "patterns": [ + "{indices}/_terms_enum" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/search-terms-enum.html" + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/termvectors.json b/src/plugins/console/server/lib/spec_definitions/json/generated/termvectors.json index d94cffc38b7af..53ea7a9d6cbf6 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/termvectors.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/termvectors.json @@ -14,8 +14,7 @@ "version_type": [ "internal", "external", - "external_gte", - "force" + "external_gte" ] }, "methods": [ diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/text_structure.find_structure.json b/src/plugins/console/server/lib/spec_definitions/json/generated/text_structure.find_structure.json new file mode 100644 index 0000000000000..8b5e576b495a3 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/text_structure.find_structure.json @@ -0,0 +1,32 @@ +{ + "text_structure.find_structure": { + "url_params": { + "lines_to_sample": 0, + "line_merge_size_limit": 0, + "timeout": "", + "charset": "", + "format": [ + "ndjson", + "xml", + "delimited", + "semi_structured_text" + ], + "has_header_row": "__flag__", + "column_names": [], + "delimiter": "", + "quote": "", + "should_trim_fields": "__flag__", + "grok_pattern": "", + "timestamp_field": "", + "timestamp_format": "", + "explain": "__flag__" + }, + "methods": [ + "POST" + ], + "patterns": [ + "_text_structure/find_structure" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/find-structure.html" + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/transform.cat_transform.json b/src/plugins/console/server/lib/spec_definitions/json/generated/transform.cat_transform.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/transform.cat_transform.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/transform.cat_transform.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/transform.delete_transform.json b/src/plugins/console/server/lib/spec_definitions/json/generated/transform.delete_transform.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/transform.delete_transform.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/transform.delete_transform.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/transform.get_transform.json b/src/plugins/console/server/lib/spec_definitions/json/generated/transform.get_transform.json similarity index 81% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/transform.get_transform.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/transform.get_transform.json index 2875ff8b91937..3a50ff7fc3c1c 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/transform.get_transform.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/transform.get_transform.json @@ -3,7 +3,8 @@ "url_params": { "from": 0, "size": 0, - "allow_no_match": "__flag__" + "allow_no_match": "__flag__", + "exclude_generated": "__flag__" }, "methods": [ "GET" diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/transform.get_transform_stats.json b/src/plugins/console/server/lib/spec_definitions/json/generated/transform.get_transform_stats.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/transform.get_transform_stats.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/transform.get_transform_stats.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/transform.preview_transform.json b/src/plugins/console/server/lib/spec_definitions/json/generated/transform.preview_transform.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/transform.preview_transform.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/transform.preview_transform.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/transform.put_transform.json b/src/plugins/console/server/lib/spec_definitions/json/generated/transform.put_transform.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/transform.put_transform.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/transform.put_transform.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/transform.start_transform.json b/src/plugins/console/server/lib/spec_definitions/json/generated/transform.start_transform.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/transform.start_transform.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/transform.start_transform.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/transform.stop_transform.json b/src/plugins/console/server/lib/spec_definitions/json/generated/transform.stop_transform.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/transform.stop_transform.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/transform.stop_transform.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/transform.update_transform.json b/src/plugins/console/server/lib/spec_definitions/json/generated/transform.update_transform.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/transform.update_transform.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/transform.update_transform.json diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/update.json b/src/plugins/console/server/lib/spec_definitions/json/generated/update.json index 43945dfada35c..4dd33978a77b8 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/update.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/update.json @@ -15,7 +15,8 @@ "routing": "", "timeout": "", "if_seq_no": "", - "if_primary_term": "" + "if_primary_term": "", + "require_alias": "__flag__" }, "methods": [ "POST" diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/update_by_query.json b/src/plugins/console/server/lib/spec_definitions/json/generated/update_by_query.json index 596f8f8b83963..b4b88312e638e 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/update_by_query.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/update_by_query.json @@ -33,7 +33,6 @@ "dfs_query_then_fetch" ], "search_timeout": "", - "size": "", "max_docs": "all documents", "sort": [], "_source": [], diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/watcher.ack_watch.json b/src/plugins/console/server/lib/spec_definitions/json/generated/watcher.ack_watch.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/watcher.ack_watch.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/watcher.ack_watch.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/watcher.activate_watch.json b/src/plugins/console/server/lib/spec_definitions/json/generated/watcher.activate_watch.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/watcher.activate_watch.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/watcher.activate_watch.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/watcher.deactivate_watch.json b/src/plugins/console/server/lib/spec_definitions/json/generated/watcher.deactivate_watch.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/watcher.deactivate_watch.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/watcher.deactivate_watch.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/watcher.delete_watch.json b/src/plugins/console/server/lib/spec_definitions/json/generated/watcher.delete_watch.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/watcher.delete_watch.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/watcher.delete_watch.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/watcher.execute_watch.json b/src/plugins/console/server/lib/spec_definitions/json/generated/watcher.execute_watch.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/watcher.execute_watch.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/watcher.execute_watch.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/watcher.get_watch.json b/src/plugins/console/server/lib/spec_definitions/json/generated/watcher.get_watch.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/watcher.get_watch.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/watcher.get_watch.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/watcher.put_watch.json b/src/plugins/console/server/lib/spec_definitions/json/generated/watcher.put_watch.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/watcher.put_watch.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/watcher.put_watch.json diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/watcher.query_watches.json b/src/plugins/console/server/lib/spec_definitions/json/generated/watcher.query_watches.json new file mode 100644 index 0000000000000..ea6f03a5672c1 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/watcher.query_watches.json @@ -0,0 +1,12 @@ +{ + "watcher.query_watches": { + "methods": [ + "GET", + "POST" + ], + "patterns": [ + "_watcher/_query/watches" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/watcher-api-query-watches.html" + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/watcher.start.json b/src/plugins/console/server/lib/spec_definitions/json/generated/watcher.start.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/watcher.start.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/watcher.start.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/watcher.stats.json b/src/plugins/console/server/lib/spec_definitions/json/generated/watcher.stats.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/watcher.stats.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/watcher.stats.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/watcher.stop.json b/src/plugins/console/server/lib/spec_definitions/json/generated/watcher.stop.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/watcher.stop.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/watcher.stop.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/xpack.info.json b/src/plugins/console/server/lib/spec_definitions/json/generated/xpack.info.json similarity index 78% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/xpack.info.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/xpack.info.json index 5b555dc129390..da92c12c5fd69 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/xpack.info.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/xpack.info.json @@ -1,7 +1,8 @@ { "xpack.info": { "url_params": { - "categories": [] + "categories": [], + "accept_enterprise": "__flag__" }, "methods": [ "GET" diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/xpack.ssl.certificates.json b/src/plugins/console/server/lib/spec_definitions/json/generated/xpack.ssl.certificates.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/xpack.ssl.certificates.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/xpack.ssl.certificates.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/xpack.usage.json b/src/plugins/console/server/lib/spec_definitions/json/generated/xpack.usage.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/xpack.usage.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/xpack.usage.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/async_search.submit.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/async_search.submit.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/async_search.submit.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/async_search.submit.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ccr.follow.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/ccr.follow.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ccr.follow.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/ccr.follow.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ccr.forget_follower.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/ccr.forget_follower.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ccr.forget_follower.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/ccr.forget_follower.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ccr.put_auto_follow_pattern.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/ccr.put_auto_follow_pattern.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ccr.put_auto_follow_pattern.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/ccr.put_auto_follow_pattern.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ccr.resume_follow.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/ccr.resume_follow.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ccr.resume_follow.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/ccr.resume_follow.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/enrich.put_policy.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/enrich.put_policy.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/enrich.put_policy.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/enrich.put_policy.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ilm.move_to_step.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/ilm.move_to_step.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ilm.move_to_step.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/ilm.move_to_step.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ilm.put_lifecycle.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/ilm.put_lifecycle.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ilm.put_lifecycle.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/ilm.put_lifecycle.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.evaluate_data_frame.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/ml.evaluate_data_frame.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.evaluate_data_frame.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/ml.evaluate_data_frame.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.explain_data_frame_analytics.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/ml.explain_data_frame_analytics.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.explain_data_frame_analytics.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/ml.explain_data_frame_analytics.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.get_buckets.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/ml.get_buckets.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.get_buckets.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/ml.get_buckets.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.get_calendar_events.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/ml.get_calendar_events.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.get_calendar_events.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/ml.get_calendar_events.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.get_calendars.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/ml.get_calendars.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.get_calendars.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/ml.get_calendars.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.get_categories.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/ml.get_categories.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.get_categories.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/ml.get_categories.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.get_influencers.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/ml.get_influencers.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.get_influencers.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/ml.get_influencers.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.get_model_snapshots.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/ml.get_model_snapshots.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.get_model_snapshots.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/ml.get_model_snapshots.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.get_overall_buckets.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/ml.get_overall_buckets.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.get_overall_buckets.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/ml.get_overall_buckets.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.get_records.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/ml.get_records.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.get_records.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/ml.get_records.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.post_calendar_events.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/ml.post_calendar_events.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.post_calendar_events.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/ml.post_calendar_events.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.put_calendar.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/ml.put_calendar.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.put_calendar.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/ml.put_calendar.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.put_data_frame_analytics.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/ml.put_data_frame_analytics.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.put_data_frame_analytics.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/ml.put_data_frame_analytics.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.put_datafeed.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/ml.put_datafeed.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.put_datafeed.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/ml.put_datafeed.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.put_job.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/ml.put_job.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.put_job.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/ml.put_job.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.put_trained_model.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/ml.put_trained_model.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.put_trained_model.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/ml.put_trained_model.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.revert_model_snapshot.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/ml.revert_model_snapshot.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.revert_model_snapshot.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/ml.revert_model_snapshot.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.update_datafeed.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/ml.update_datafeed.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.update_datafeed.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/ml.update_datafeed.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.update_job.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/ml.update_job.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.update_job.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/ml.update_job.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.update_model_snapshot.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/ml.update_model_snapshot.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/ml.update_model_snapshot.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/ml.update_model_snapshot.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/security.authenticate.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/security.authenticate.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/security.authenticate.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/security.authenticate.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/security.change_password.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/security.change_password.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/security.change_password.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/security.change_password.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/security.create_api_key.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/security.create_api_key.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/security.create_api_key.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/security.create_api_key.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/security.get_token.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/security.get_token.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/security.get_token.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/security.get_token.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/security.has_privileges.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/security.has_privileges.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/security.has_privileges.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/security.has_privileges.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/security.invalidate_api_key.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/security.invalidate_api_key.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/security.invalidate_api_key.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/security.invalidate_api_key.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/security.invalidate_token.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/security.invalidate_token.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/security.invalidate_token.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/security.invalidate_token.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/security.put_role.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/security.put_role.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/security.put_role.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/security.put_role.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/security.put_role_mapping.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/security.put_role_mapping.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/security.put_role_mapping.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/security.put_role_mapping.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/security.put_user.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/security.put_user.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/security.put_user.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/security.put_user.json diff --git a/src/plugins/console/server/lib/spec_definitions/json/overrides/security.saml_authenticate.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/security.saml_authenticate.json new file mode 100644 index 0000000000000..a1d50daa70572 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/overrides/security.saml_authenticate.json @@ -0,0 +1,9 @@ +{ + "security.saml_authenticate": { + "data_autocomplete_rules": { + "content": "", + "ids": [], + "realm": "" + } + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/overrides/security.saml_complete_logout.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/security.saml_complete_logout.json new file mode 100644 index 0000000000000..b9d5b83e0424c --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/overrides/security.saml_complete_logout.json @@ -0,0 +1,10 @@ +{ + "security.saml_complete_logout": { + "data_autocomplete_rules": { + "realm": "", + "ids": [], + "query_string": "", + "content": "" + } + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/overrides/security.saml_invalidate.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/security.saml_invalidate.json new file mode 100644 index 0000000000000..f4a27d659c360 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/overrides/security.saml_invalidate.json @@ -0,0 +1,9 @@ +{ + "security.saml_invalidate": { + "data_autocomplete_rules": { + "query_string": "", + "acs": "", + "realm": "" + } + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/overrides/security.saml_logout.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/security.saml_logout.json new file mode 100644 index 0000000000000..40e5c6bc09299 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/overrides/security.saml_logout.json @@ -0,0 +1,8 @@ +{ + "security.saml_logout": { + "data_autocomplete_rules": { + "token": "", + "refresh_token": "" + } + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/overrides/security.saml_prepare_authentication.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/security.saml_prepare_authentication.json new file mode 100644 index 0000000000000..4ebdd53c95166 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/overrides/security.saml_prepare_authentication.json @@ -0,0 +1,8 @@ +{ + "security.saml_prepare_authentication": { + "data_autocomplete_rules": { + "acs": "", + "realm": "" + } + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/slm.put_lifecycle.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/slm.put_lifecycle.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/slm.put_lifecycle.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/slm.put_lifecycle.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/slm.start.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/slm.start.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/slm.start.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/slm.start.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/slm.stop.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/slm.stop.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/slm.stop.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/slm.stop.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/sql.query.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/sql.query.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/sql.query.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/sql.query.json diff --git a/src/plugins/console/server/lib/spec_definitions/json/overrides/terms_enum.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/terms_enum.json new file mode 100644 index 0000000000000..3a3681cefe072 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/overrides/terms_enum.json @@ -0,0 +1,12 @@ +{ + "terms_enum": { + "data_autocomplete_rules": { + "field": "", + "string": "", + "size": "", + "timeout": "", + "case_insensitive": { "__one_of": [true, false] }, + "index_filter": "" + } + } +} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/transform.get_transform_stats.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/transform.get_transform_stats.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/transform.get_transform_stats.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/transform.get_transform_stats.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/transform.preview_transform.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/transform.preview_transform.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/transform.preview_transform.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/transform.preview_transform.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/transform.put_transform.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/transform.put_transform.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/transform.put_transform.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/transform.put_transform.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/transform.stop_transform.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/transform.stop_transform.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/transform.stop_transform.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/transform.stop_transform.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/transform.update_transform.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/transform.update_transform.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/transform.update_transform.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/transform.update_transform.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.graph.explore.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.graph.explore.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.graph.explore.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.graph.explore.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.info.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.info.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.info.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.info.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.license.post.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.license.post.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.license.post.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.license.post.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.rollup.delete_job.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.rollup.delete_job.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.rollup.delete_job.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.rollup.delete_job.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.rollup.put_job.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.rollup.put_job.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.rollup.put_job.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.rollup.put_job.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.rollup.rollup_search.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.rollup.rollup_search.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.rollup.rollup_search.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.rollup.rollup_search.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.security.authenticate.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.security.authenticate.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.security.authenticate.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.security.authenticate.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.security.change_password.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.security.change_password.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.security.change_password.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.security.change_password.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.security.get_token.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.security.get_token.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.security.get_token.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.security.get_token.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.security.invalidate_token.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.security.invalidate_token.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.security.invalidate_token.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.security.invalidate_token.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.security.put_role.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.security.put_role.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.security.put_role.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.security.put_role.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.security.put_role_mapping.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.security.put_role_mapping.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.security.put_role_mapping.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.security.put_role_mapping.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.security.put_user.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.security.put_user.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.security.put_user.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.security.put_user.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.watcher.execute_watch.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.watcher.execute_watch.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.watcher.execute_watch.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.watcher.execute_watch.json diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.watcher.put_watch.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.watcher.put_watch.json similarity index 100% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/json/overrides/xpack.watcher.put_watch.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/xpack.watcher.put_watch.json diff --git a/src/plugins/dashboard/public/application/top_nav/save_modal.tsx b/src/plugins/dashboard/public/application/top_nav/save_modal.tsx index 79ac3917fb968..b0ed1ad0de9b6 100644 --- a/src/plugins/dashboard/public/application/top_nav/save_modal.tsx +++ b/src/plugins/dashboard/public/application/top_nav/save_modal.tsx @@ -8,6 +8,7 @@ import React, { Fragment } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; import { EuiFormRow, EuiTextArea, EuiSwitch } from '@elastic/eui'; import type { SavedObjectsTaggingApi } from '../../services/saved_objects_tagging_oss'; @@ -148,7 +149,9 @@ export class DashboardSaveModal extends React.Component { title={this.props.title} showCopyOnSave={this.props.showCopyOnSave} initialCopyOnSave={this.props.showCopyOnSave} - objectType="dashboard" + objectType={i18n.translate('dashboard.topNav.saveModal.objectType', { + defaultMessage: 'dashboard', + })} options={this.renderDashboardSaveOptions()} showDescription={false} /> diff --git a/src/plugins/data/common/constants.ts b/src/plugins/data/common/constants.ts index c6bfbfc75c290..645de0f9fed17 100644 --- a/src/plugins/data/common/constants.ts +++ b/src/plugins/data/common/constants.ts @@ -12,6 +12,8 @@ export const KIBANA_USER_QUERY_LANGUAGE_KEY = 'kibana.userQueryLanguage'; /** @public **/ export const INDEX_PATTERN_SAVED_OBJECT_TYPE = 'index-pattern'; +export type ValueSuggestionsMethod = 'terms_enum' | 'terms_agg'; + export const UI_SETTINGS = { META_FIELDS: 'metaFields', DOC_HIGHLIGHT: 'doc_table:highlight', diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts index b03e745df74a6..58cc6d0478d5e 100644 --- a/src/plugins/data/common/index_patterns/types.ts +++ b/src/plugins/data/common/index_patterns/types.ts @@ -150,9 +150,17 @@ export type AggregationRestrictions = Record< time_zone?: string; } >; + export interface TypeMeta { aggs?: Record; - [key: string]: any; + params?: { + rollup_index: string; + }; +} + +export enum IndexPatternType { + DEFAULT = 'default', + ROLLUP = 'rollup', } export type FieldSpecConflictDescriptions = Record; diff --git a/src/plugins/data/common/search/expressions/esaggs/request_handler.ts b/src/plugins/data/common/search/expressions/esaggs/request_handler.ts index 61193c52a5e74..bf931966f5bae 100644 --- a/src/plugins/data/common/search/expressions/esaggs/request_handler.ts +++ b/src/plugins/data/common/search/expressions/esaggs/request_handler.ts @@ -100,8 +100,6 @@ export const handleRequest = async ({ requestSearchSource.setField('filter', filters); requestSearchSource.setField('query', query); - inspectorAdapters.requests?.reset(); - const { rawResponse: response } = await requestSearchSource .fetch$({ abortSignal, diff --git a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/value.ts b/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/value.ts index f8fc9d165fc6b..cee7d37a1a12d 100644 --- a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/value.ts +++ b/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/value.ts @@ -35,7 +35,7 @@ export const setupGetValueSuggestions: KqlQuerySuggestionProvider = ( .getStartServices() .then(([_, __, dataStart]) => dataStart.autocomplete); return async ( - { indexPatterns, boolFilter, useTimeRange, signal }, + { indexPatterns, boolFilter, useTimeRange, signal, method }, { start, end, prefix, suffix, fieldName, nestedPath } ): Promise => { const fullFieldName = nestedPath ? `${nestedPath}.${fieldName}` : fieldName; @@ -59,6 +59,7 @@ export const setupGetValueSuggestions: KqlQuerySuggestionProvider = ( boolFilter, useTimeRange, signal, + method, }).then((valueSuggestions) => { const quotedValues = valueSuggestions.map((value) => typeof value === 'string' ? `"${escapeQuotes(value)}"` : `${value}` diff --git a/src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts b/src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts index 449b056a60c75..ebd90c7e92d75 100644 --- a/src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts +++ b/src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { ValueSuggestionsMethod } from '../../../common'; import { IFieldType, IIndexPattern } from '../../../common/index_patterns'; export enum QuerySuggestionTypes { @@ -30,6 +31,7 @@ export interface QuerySuggestionGetFnArgs { signal?: AbortSignal; useTimeRange?: boolean; boolFilter?: any; + method?: ValueSuggestionsMethod; } /** @public **/ diff --git a/src/plugins/data/public/autocomplete/providers/value_suggestion_provider.ts b/src/plugins/data/public/autocomplete/providers/value_suggestion_provider.ts index 3dda97566da5a..05dc38b8e5ac5 100644 --- a/src/plugins/data/public/autocomplete/providers/value_suggestion_provider.ts +++ b/src/plugins/data/public/autocomplete/providers/value_suggestion_provider.ts @@ -9,7 +9,13 @@ import dateMath from '@elastic/datemath'; import { memoize } from 'lodash'; import { CoreSetup } from 'src/core/public'; -import { IIndexPattern, IFieldType, UI_SETTINGS, buildQueryFromFilters } from '../../../common'; +import { + IIndexPattern, + IFieldType, + UI_SETTINGS, + buildQueryFromFilters, + ValueSuggestionsMethod, +} from '../../../common'; import { TimefilterSetup } from '../../query'; import { AutocompleteUsageCollector } from '../collectors'; @@ -22,6 +28,7 @@ interface ValueSuggestionsGetFnArgs { useTimeRange?: boolean; boolFilter?: any[]; signal?: AbortSignal; + method?: ValueSuggestionsMethod; } const getAutocompleteTimefilter = ( @@ -54,12 +61,25 @@ export const setupValueSuggestionProvider = ( } const requestSuggestions = memoize( - (index: string, field: IFieldType, query: string, filters: any = [], signal?: AbortSignal) => { + ( + index: string, + field: IFieldType, + query: string, + filters: any = [], + signal?: AbortSignal, + method?: ValueSuggestionsMethod + ) => { usageCollector?.trackRequest(); return core.http .fetch(`/api/kibana/suggestions/values/${index}`, { method: 'POST', - body: JSON.stringify({ query, field: field.name, fieldMeta: field?.toSpec?.(), filters }), + body: JSON.stringify({ + query, + field: field.name, + fieldMeta: field?.toSpec?.(), + filters, + method, + }), signal, }) .then((r) => { @@ -77,6 +97,7 @@ export const setupValueSuggestionProvider = ( useTimeRange, boolFilter, signal, + method, }: ValueSuggestionsGetFnArgs): Promise => { const shouldSuggestValues = core!.uiSettings.get( UI_SETTINGS.FILTERS_EDITOR_SUGGEST_VALUES @@ -98,7 +119,7 @@ export const setupValueSuggestionProvider = ( const filters = [...(boolFilter ? boolFilter : []), ...filterQuery]; try { usageCollector?.trackCall(); - return await requestSuggestions(title, field, query, filters, signal); + return await requestSuggestions(title, field, query, filters, signal, method); } catch (e) { if (!signal?.aborted) { usageCollector?.trackError(); diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index e9e50ebfaf138..9af1cbf95d94d 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -269,6 +269,7 @@ export { IndexPatternLoadExpressionFunctionDefinition, fieldList, INDEX_PATTERN_SAVED_OBJECT_TYPE, + IndexPatternType, } from '../common'; export { DuplicateIndexPatternError } from '../common/index_patterns/errors'; diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index b8af7c12d57fc..4508ad5948927 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -1688,14 +1688,26 @@ export class IndexPatternsService { updateSavedObject(indexPattern: IndexPattern, saveAttempts?: number, ignoreErrors?: boolean): Promise; } +// Warning: (ae-missing-release-tag) "IndexPatternType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export enum IndexPatternType { + // (undocumented) + DEFAULT = "default", + // (undocumented) + ROLLUP = "rollup" +} + // Warning: (ae-missing-release-tag) "TypeMeta" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) export interface IndexPatternTypeMeta { - // (undocumented) - [key: string]: any; // (undocumented) aggs?: Record; + // (undocumented) + params?: { + rollup_index: string; + }; } // Warning: (ae-missing-release-tag) "injectReferences" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -2166,6 +2178,10 @@ export interface QuerySuggestionGetFnArgs { indexPatterns: IIndexPattern[]; // (undocumented) language: string; + // Warning: (ae-forgotten-export) The symbol "ValueSuggestionsMethod" needs to be exported by the entry point index.d.ts + // + // (undocumented) + method?: ValueSuggestionsMethod; // (undocumented) query: string; // (undocumented) @@ -2766,20 +2782,20 @@ export interface WaitUntilNextSessionCompletesOptions { // src/plugins/data/public/index.ts:238:27 - (ae-forgotten-export) The symbol "validateIndexPattern" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:238:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:238:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:409:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:409:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:409:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:411:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:412:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:421:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:422:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:423:1 - (ae-forgotten-export) The symbol "IpAddress" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:424:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:428:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:429:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:432:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:433:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:436:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:410:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:410:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:410:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:412:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:413:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:422:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:423:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:424:1 - (ae-forgotten-export) The symbol "IpAddress" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:425:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:429:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:430:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:433:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:434:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:437:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // src/plugins/data/public/query/state_sync/connect_to_query_state.ts:34:5 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts // src/plugins/data/public/search/session/session_service.ts:62:5 - (ae-forgotten-export) The symbol "UrlGeneratorStateMapping" needs to be exported by the entry point index.d.ts diff --git a/src/plugins/data/server/autocomplete/value_suggestions_route.ts b/src/plugins/data/server/autocomplete/value_suggestions_route.ts index bd622d0151c93..42f2c4d4e6341 100644 --- a/src/plugins/data/server/autocomplete/value_suggestions_route.ts +++ b/src/plugins/data/server/autocomplete/value_suggestions_route.ts @@ -33,6 +33,9 @@ export function registerValueSuggestionsRoute(router: IRouter, config$: Observab query: schema.string(), filters: schema.maybe(schema.any()), fieldMeta: schema.maybe(schema.any()), + method: schema.maybe( + schema.oneOf([schema.literal('terms_agg'), schema.literal('terms_enum')]) + ), }, { unknowns: 'allow' } ), @@ -40,13 +43,13 @@ export function registerValueSuggestionsRoute(router: IRouter, config$: Observab }, async (context, request, response) => { const config = await config$.pipe(first()).toPromise(); - const { field: fieldName, query, filters, fieldMeta } = request.body; + const { field: fieldName, query, filters, fieldMeta, method } = request.body; const { index } = request.params; const abortSignal = getRequestAbortedSignal(request.events.aborted$); try { const fn = - config.autocomplete.valueSuggestions.method === 'terms_enum' + (method ?? config.autocomplete.valueSuggestions.method) === 'terms_enum' ? termsEnumSuggestions : termsAggSuggestions; const body = await fn( diff --git a/src/plugins/discover/public/application/apps/main/components/top_nav/on_save_search.tsx b/src/plugins/discover/public/application/apps/main/components/top_nav/on_save_search.tsx index f4b969e977254..c3d1df096c32f 100644 --- a/src/plugins/discover/public/application/apps/main/components/top_nav/on_save_search.tsx +++ b/src/plugins/discover/public/application/apps/main/components/top_nav/on_save_search.tsx @@ -135,7 +135,9 @@ export async function onSaveSearch({ onClose={() => {}} title={savedSearch.title} showCopyOnSave={!!savedSearch.id} - objectType="search" + objectType={i18n.translate('discover.localMenu.saveSaveSearchObjectType', { + defaultMessage: 'search', + })} description={i18n.translate('discover.localMenu.saveSaveSearchDescription', { defaultMessage: 'Save your Discover search so you can use it in visualizations and dashboards', diff --git a/src/plugins/discover/public/application/apps/main/services/use_saved_search.ts b/src/plugins/discover/public/application/apps/main/services/use_saved_search.ts index 8c847b54078eb..b449d35dca9ad 100644 --- a/src/plugins/discover/public/application/apps/main/services/use_saved_search.ts +++ b/src/plugins/discover/public/application/apps/main/services/use_saved_search.ts @@ -279,13 +279,21 @@ export const useSavedSearch = ({ ).pipe(debounceTime(100)); const subscription = fetch$.subscribe((val) => { - fetchAll(val === 'reset'); + try { + fetchAll(val === 'reset'); + } catch (error) { + data$.next({ + state: FetchStatus.ERROR, + fetchError: error, + }); + } }); return () => { subscription.unsubscribe(); }; }, [ + data$, data.query.queryString, filterManager, refetch$, diff --git a/src/plugins/discover/public/application/components/table/table.test.tsx b/src/plugins/discover/public/application/components/table/table.test.tsx index a38a9e41aa242..5a8d3e7d2db46 100644 --- a/src/plugins/discover/public/application/components/table/table.test.tsx +++ b/src/plugins/discover/public/application/components/table/table.test.tsx @@ -334,6 +334,33 @@ describe('DocViewTable at Discover Doc with Fields API', () => { }, }, }, + { + name: 'city', + displayName: 'city', + type: 'keyword', + isMapped: true, + readFromDocValues: true, + searchable: true, + shortDotsEnable: false, + scripted: false, + filterable: false, + }, + { + name: 'city.raw', + displayName: 'city.raw', + type: 'string', + isMapped: true, + spec: { + subType: { + multi: { + parent: 'city', + }, + }, + }, + shortDotsEnable: false, + scripted: false, + filterable: false, + }, ], }, metaFields: ['_index', '_type', '_score', '_id'], @@ -380,6 +407,7 @@ describe('DocViewTable at Discover Doc with Fields API', () => { customer_first_name: 'Betty', 'customer_first_name.keyword': 'Betty', 'customer_first_name.nickname': 'Betsy', + 'city.raw': 'Los Angeles', }, }; const props = { @@ -417,6 +445,8 @@ describe('DocViewTable at Discover Doc with Fields API', () => { findTestSubject(component, 'tableDocViewRow-customer_first_name.nickname-multifieldBadge') .length ).toBe(1); + + expect(findTestSubject(component, 'tableDocViewRow-city.raw').length).toBe(1); }); it('does not render multifield rows if showMultiFields flag is not set', () => { @@ -449,5 +479,7 @@ describe('DocViewTable at Discover Doc with Fields API', () => { findTestSubject(component, 'tableDocViewRow-customer_first_name.nickname-multifieldBadge') .length ).toBe(0); + + expect(findTestSubject(component, 'tableDocViewRow-city.raw').length).toBe(1); }); }); diff --git a/src/plugins/discover/public/application/components/table/table.tsx b/src/plugins/discover/public/application/components/table/table.tsx index 065b146105a65..2d261805d8eb8 100644 --- a/src/plugins/discover/public/application/components/table/table.tsx +++ b/src/plugins/discover/public/application/components/table/table.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import { EuiInMemoryTable } from '@elastic/eui'; import { IndexPattern, IndexPatternField } from '../../../../../data/public'; import { SHOW_MULTIFIELDS } from '../../../../common'; @@ -60,6 +60,8 @@ export const DocViewerTable = ({ indexPattern?.fields, ]); + const [childParentFieldsMap] = useState({} as Record); + const formattedHit = useMemo(() => indexPattern?.formatHit(hit, 'html'), [hit, indexPattern]); const tableColumns = useMemo(() => { @@ -92,11 +94,21 @@ export const DocViewerTable = ({ } const flattened = indexPattern.flattenHit(hit); + Object.keys(flattened).forEach((key) => { + const field = mapping(key); + if (field && field.spec?.subType?.multi?.parent) { + childParentFieldsMap[field.name] = field.spec.subType.multi.parent; + } + }); const items: FieldRecord[] = Object.keys(flattened) .filter((fieldName) => { const fieldMapping = mapping(fieldName); const isMultiField = !!fieldMapping?.spec?.subType?.multi; - return isMultiField ? showMultiFields : true; + if (!isMultiField) { + return true; + } + const parent = childParentFieldsMap[fieldName]; + return showMultiFields || (parent && !flattened.hasOwnProperty(parent)); }) .sort((fieldA, fieldB) => { const mappingA = mapping(fieldA); diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_array.ts b/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_array.ts index 1152290f7d553..0a6f2cc2c2b18 100644 --- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_array.ts +++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_array.ts @@ -62,7 +62,7 @@ export const UseArray = ({ const uniqueId = useRef(0); const form = useFormContext(); - const { __getFieldDefaultValue } = form; + const { getFieldDefaultValue } = form; const getNewItemAtIndex = useCallback( (index: number): ArrayItem => ({ @@ -75,7 +75,7 @@ export const UseArray = ({ const fieldDefaultValue = useMemo(() => { const defaultValues = readDefaultValueOnForm - ? (__getFieldDefaultValue(path) as any[]) + ? (getFieldDefaultValue(path) as any[]) : undefined; const getInitialItemsFromValues = (values: any[]): ArrayItem[] => @@ -88,13 +88,7 @@ export const UseArray = ({ return defaultValues ? getInitialItemsFromValues(defaultValues) : new Array(initialNumberOfItems).fill('').map((_, i) => getNewItemAtIndex(i)); - }, [ - path, - initialNumberOfItems, - readDefaultValueOnForm, - __getFieldDefaultValue, - getNewItemAtIndex, - ]); + }, [path, initialNumberOfItems, readDefaultValueOnForm, getFieldDefaultValue, getNewItemAtIndex]); // Create a new hook field with the "isIncludedInOutput" set to false so we don't use its value to build the final form data. // Apart from that the field behaves like a normal field and is hooked into the form validation lifecycle. diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_field.tsx b/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_field.tsx index 3a5fbaba8f3b8..45fa2e977a6c7 100644 --- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_field.tsx +++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_field.tsx @@ -59,8 +59,7 @@ function UseFieldComp(props: Props { + describe('field.validate()', () => { + const EMPTY_VALUE = ' '; + + test('It should not invalidate a field with arrayItem validation when isBlocking is false', async () => { + let fieldHook: FieldHook; + + const TestField = ({ field }: { field: FieldHook }) => { + fieldHook = field; + return null; + }; + + const TestForm = () => { + const { form } = useForm(); + + return ( +
+ + + ); + }; + + registerTestBed(TestForm)(); + + let validateResponse: FieldValidateResponse; + + await act(async () => { + validateResponse = await fieldHook!.validate({ + value: EMPTY_VALUE, + validationType: VALIDATION_TYPES.ARRAY_ITEM, + }); + }); + + // validation fails for ARRAY_ITEM with a non-blocking validation error + expect(validateResponse!).toEqual({ + isValid: false, + errors: [ + { + code: 'ERR_FIELD_MISSING', + path: 'test-path', + message: 'error-message', + __isBlocking__: false, + validationType: 'arrayItem', + }, + ], + }); + + // expect the field to be valid because the validation error is non-blocking + expect(fieldHook!.isValid).toBe(true); + }); + + test('It should invalidate an arrayItem field when isBlocking is true', async () => { + let fieldHook: FieldHook; + + const TestField = ({ field }: { field: FieldHook }) => { + fieldHook = field; + return null; + }; + + const TestForm = () => { + const { form } = useForm(); + + return ( +
+ + + ); + }; + + registerTestBed(TestForm)(); + + let validateResponse: FieldValidateResponse; + + await act(async () => { + validateResponse = await fieldHook!.validate({ + value: EMPTY_VALUE, + validationType: VALIDATION_TYPES.ARRAY_ITEM, + }); + }); + + // validation fails for ARRAY_ITEM with a blocking validation error + expect(validateResponse!).toEqual({ + isValid: false, + errors: [ + { + code: 'ERR_FIELD_MISSING', + path: 'test-path', + message: 'error-message', + __isBlocking__: true, + validationType: 'arrayItem', + }, + ], + }); + + // expect the field to be invalid because the validation error is blocking + expect(fieldHook!.isValid).toBe(false); + }); + }); +}); diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_field.ts b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_field.ts index 0cf1bb3601667..77bb17d7b9e60 100644 --- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_field.ts +++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_field.ts @@ -15,6 +15,7 @@ import { FieldValidateResponse, ValidationError, FormData, + ValidationConfig, } from '../types'; import { FIELD_TYPES, VALIDATION_TYPES } from '../constants'; @@ -189,10 +190,12 @@ export const useField = ( { formData, value: valueToValidate, + onlyBlocking: runOnlyBlockingValidations, validationTypeToValidate, }: { formData: any; value: I; + onlyBlocking: boolean; validationTypeToValidate?: string; }, clearFieldErrors: FieldHook['clearErrors'] @@ -203,10 +206,31 @@ export const useField = ( // By default, for fields that have an asynchronous validation // we will clear the errors as soon as the field value changes. - clearFieldErrors([VALIDATION_TYPES.FIELD, VALIDATION_TYPES.ASYNC]); + clearFieldErrors([ + validationTypeToValidate ?? VALIDATION_TYPES.FIELD, + VALIDATION_TYPES.ASYNC, + ]); cancelInflightValidation(); + const doByPassValidation = ({ + type: validationType, + isBlocking, + }: ValidationConfig) => { + if ( + typeof validationTypeToValidate !== 'undefined' && + validationType !== validationTypeToValidate + ) { + return true; + } + + if (runOnlyBlockingValidations && isBlocking === false) { + return true; + } + + return false; + }; + const runAsync = async () => { const validationErrors: ValidationError[] = []; @@ -219,10 +243,7 @@ export const useField = ( type: validationType = VALIDATION_TYPES.FIELD, } = validation; - if ( - typeof validationTypeToValidate !== 'undefined' && - validationType !== validationTypeToValidate - ) { + if (doByPassValidation(validation)) { continue; } @@ -265,10 +286,7 @@ export const useField = ( type: validationType = VALIDATION_TYPES.FIELD, } = validation; - if ( - typeof validationTypeToValidate !== 'undefined' && - validationType !== validationTypeToValidate - ) { + if (doByPassValidation(validation)) { continue; } @@ -344,6 +362,7 @@ export const useField = ( formData = __getFormData$().value, value: valueToValidate = value, validationType, + onlyBlocking = false, } = validationData; setIsValidated(true); @@ -377,6 +396,7 @@ export const useField = ( formData, value: valueToValidate, validationTypeToValidate: validationType, + onlyBlocking, }, clearErrors ); diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form.test.tsx b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form.test.tsx index 40fc179c73c3b..92a9876f1cd30 100644 --- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form.test.tsx +++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form.test.tsx @@ -10,7 +10,7 @@ import React, { useEffect } from 'react'; import { act } from 'react-dom/test-utils'; import { registerTestBed, getRandomString, TestBed } from '../shared_imports'; - +import { emptyField } from '../../helpers/field_validators'; import { Form, UseField } from '../components'; import { FormSubmitHandler, @@ -18,7 +18,8 @@ import { FormHook, ValidationFunc, FieldConfig, -} from '../types'; + VALIDATION_TYPES, +} from '..'; import { useForm } from './use_form'; interface MyForm { @@ -501,4 +502,74 @@ describe('useForm() hook', () => { expect(isValid).toBeUndefined(); // Make sure it is "undefined" and not "false". }); }); + + describe('form.validate()', () => { + test('should not invalidate a field with arrayItem validation when validating a form', async () => { + const TestComp = () => { + const { form } = useForm(); + formHook = form; + + return ( +
+ + + ); + }; + + registerTestBed(TestComp)(); + + let isValid: boolean = false; + + await act(async () => { + isValid = await formHook!.validate(); + }); + + expect(isValid).toBe(true); + }); + + test('should invalidate a field with a blocking arrayItem validation when validating a form', async () => { + const TestComp = () => { + const { form } = useForm(); + formHook = form; + + return ( +
+ + + ); + }; + + registerTestBed(TestComp)(); + + let isValid: boolean = false; + + await act(async () => { + isValid = await formHook!.validate(); + }); + + expect(isValid).toBe(false); + }); + }); }); diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form.ts b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form.ts index fb334afb22b13..dcf2cb37d6542 100644 --- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form.ts +++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form.ts @@ -151,14 +151,14 @@ export function useForm( }, [fieldsToArray]); const validateFields: FormHook['__validateFields'] = useCallback( - async (fieldNames) => { + async (fieldNames, onlyBlocking = false) => { const fieldsToValidate = fieldNames .map((name) => fieldsRefs.current[name]) .filter((field) => field !== undefined); const formData = getFormData$().value; const validationResult = await Promise.all( - fieldsToValidate.map((field) => field.validate({ formData })) + fieldsToValidate.map((field) => field.validate({ formData, onlyBlocking })) ); if (isMounted.current === false) { @@ -257,11 +257,6 @@ export function useForm( [getFormData$, updateFormData$, fieldsToArray] ); - const getFieldDefaultValue: FormHook['__getFieldDefaultValue'] = useCallback( - (fieldName) => get(defaultValueDeserialized.current, fieldName), - [] - ); - const readFieldConfigFromSchema: FormHook['__readFieldConfigFromSchema'] = useCallback( (fieldName) => { const config = (get(schema ?? {}, fieldName) as FieldConfig) || {}; @@ -315,7 +310,8 @@ export function useForm( if (fieldsToValidate.length === 0) { isFormValid = fieldsArray.every(isFieldValid); } else { - ({ isFormValid } = await validateFields(fieldsToValidate.map((field) => field.path))); + const fieldPathsToValidate = fieldsToValidate.map((field) => field.path); + ({ isFormValid } = await validateFields(fieldPathsToValidate, true)); } setIsValid(isFormValid); @@ -338,6 +334,11 @@ export function useForm( const getFields: FormHook['getFields'] = useCallback(() => fieldsRefs.current, []); + const getFieldDefaultValue: FormHook['getFieldDefaultValue'] = useCallback( + (fieldName) => get(defaultValueDeserialized.current, fieldName), + [] + ); + const submit: FormHook['submit'] = useCallback( async (e) => { if (e) { @@ -430,6 +431,7 @@ export function useForm( setFieldValue, setFieldErrors, getFields, + getFieldDefaultValue, getFormData, getErrors, reset, @@ -438,7 +440,6 @@ export function useForm( __updateFormDataAt: updateFormDataAt, __updateDefaultValueAt: updateDefaultValueAt, __readFieldConfigFromSchema: readFieldConfigFromSchema, - __getFieldDefaultValue: getFieldDefaultValue, __addField: addField, __removeField: removeField, __validateFields: validateFields, diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/types.ts b/src/plugins/es_ui_shared/static/forms/hook_form_lib/types.ts index 75c918d4340f2..4e9ff29f0cdd3 100644 --- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/types.ts +++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/types.ts @@ -36,6 +36,8 @@ export interface FormHook setFieldErrors: (fieldName: string, errors: ValidationError[]) => void; /** Access the fields on the form. */ getFields: () => FieldsMap; + /** Access the defaultValue for a specific field */ + getFieldDefaultValue: (path: string) => unknown; /** * Return the form data. It accepts an optional options object with an `unflatten` parameter (defaults to `true`). * If you are only interested in the raw form data, pass `unflatten: false` to the handler @@ -53,12 +55,13 @@ export interface FormHook __addField: (field: FieldHook) => void; __removeField: (fieldNames: string | string[]) => void; __validateFields: ( - fieldNames: string[] + fieldNames: string[], + /** Run only blocking validations */ + onlyBlocking?: boolean ) => Promise<{ areFieldsValid: boolean; isFormValid: boolean | undefined }>; __updateFormDataAt: (field: string, value: unknown) => void; __updateDefaultValueAt: (field: string, value: unknown) => void; __readFieldConfigFromSchema: (field: string) => FieldConfig; - __getFieldDefaultValue: (path: string) => unknown; } export type FormSchema = { @@ -137,6 +140,7 @@ export interface FieldHook { formData?: any; value?: I; validationType?: string; + onlyBlocking?: boolean; }) => FieldValidateResponse | Promise; reset: (options?: { resetValue?: boolean; defaultValue?: T }) => unknown | undefined; // Flag to indicate if the field value will be included in the form data outputted diff --git a/src/plugins/expression_error/.storybook/main.js b/src/plugins/expression_error/.storybook/main.js new file mode 100644 index 0000000000000..742239e638b8a --- /dev/null +++ b/src/plugins/expression_error/.storybook/main.js @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +// eslint-disable-next-line import/no-commonjs +module.exports = require('@kbn/storybook').defaultConfig; diff --git a/src/plugins/expression_error/README.md b/src/plugins/expression_error/README.md new file mode 100755 index 0000000000000..5e22d8fc652c7 --- /dev/null +++ b/src/plugins/expression_error/README.md @@ -0,0 +1,9 @@ +# expressionRevealImage + +Expression Error plugin adds an `error` renderer to the expression plugin. The renderer will display the error image. + +--- + +## Development + +See the [kibana contributing guide](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md) for instructions setting up your development environment. diff --git a/src/plugins/expression_error/common/constants.ts b/src/plugins/expression_error/common/constants.ts new file mode 100644 index 0000000000000..3a522d200090d --- /dev/null +++ b/src/plugins/expression_error/common/constants.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +export const PLUGIN_ID = 'expressionError'; +export const PLUGIN_NAME = 'expressionError'; + +export const JSON = 'JSON'; diff --git a/src/plugins/expression_error/common/index.ts b/src/plugins/expression_error/common/index.ts new file mode 100755 index 0000000000000..d8989abcc3d6f --- /dev/null +++ b/src/plugins/expression_error/common/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './constants'; diff --git a/src/plugins/expression_error/common/types/expression_renderers.ts b/src/plugins/expression_error/common/types/expression_renderers.ts new file mode 100644 index 0000000000000..25a9d5edac4ad --- /dev/null +++ b/src/plugins/expression_error/common/types/expression_renderers.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export type OriginString = 'bottom' | 'left' | 'top' | 'right'; + +export interface ErrorRendererConfig { + error: Error; +} + +export interface NodeDimensions { + width: number; + height: number; +} diff --git a/src/plugins/expression_error/common/types/index.ts b/src/plugins/expression_error/common/types/index.ts new file mode 100644 index 0000000000000..22961a0dc2fe0 --- /dev/null +++ b/src/plugins/expression_error/common/types/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './expression_renderers'; diff --git a/src/plugins/expression_error/jest.config.js b/src/plugins/expression_error/jest.config.js new file mode 100644 index 0000000000000..64f3e9292ff07 --- /dev/null +++ b/src/plugins/expression_error/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/expression_error'], +}; diff --git a/src/plugins/expression_error/kibana.json b/src/plugins/expression_error/kibana.json new file mode 100755 index 0000000000000..9d8dd566d5b3a --- /dev/null +++ b/src/plugins/expression_error/kibana.json @@ -0,0 +1,10 @@ +{ + "id": "expressionError", + "version": "1.0.0", + "kibanaVersion": "kibana", + "server": false, + "ui": true, + "requiredPlugins": ["expressions", "presentationUtil"], + "optionalPlugins": [], + "requiredBundles": [] +} diff --git a/x-pack/plugins/canvas/public/components/debug/__stories__/__snapshots__/debug.stories.storyshot b/src/plugins/expression_error/public/components/debug/__stories__/__snapshots__/debug.stories.storyshot similarity index 100% rename from x-pack/plugins/canvas/public/components/debug/__stories__/__snapshots__/debug.stories.storyshot rename to src/plugins/expression_error/public/components/debug/__stories__/__snapshots__/debug.stories.storyshot diff --git a/x-pack/plugins/canvas/public/components/debug/__stories__/debug.stories.tsx b/src/plugins/expression_error/public/components/debug/__stories__/debug.stories.tsx similarity index 72% rename from x-pack/plugins/canvas/public/components/debug/__stories__/debug.stories.tsx rename to src/plugins/expression_error/public/components/debug/__stories__/debug.stories.tsx index f29ab4b7bfda4..7dce5e8f1862b 100644 --- a/x-pack/plugins/canvas/public/components/debug/__stories__/debug.stories.tsx +++ b/src/plugins/expression_error/public/components/debug/__stories__/debug.stories.tsx @@ -1,8 +1,9 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ import React from 'react'; diff --git a/x-pack/plugins/canvas/public/components/debug/__stories__/helpers.tsx b/src/plugins/expression_error/public/components/debug/__stories__/helpers.tsx similarity index 99% rename from x-pack/plugins/canvas/public/components/debug/__stories__/helpers.tsx rename to src/plugins/expression_error/public/components/debug/__stories__/helpers.tsx index 38ff5977254f9..666731e199b4d 100644 --- a/x-pack/plugins/canvas/public/components/debug/__stories__/helpers.tsx +++ b/src/plugins/expression_error/public/components/debug/__stories__/helpers.tsx @@ -1,8 +1,9 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ export const largePayload = { diff --git a/x-pack/plugins/canvas/public/components/debug/debug.scss b/src/plugins/expression_error/public/components/debug/debug.scss similarity index 100% rename from x-pack/plugins/canvas/public/components/debug/debug.scss rename to src/plugins/expression_error/public/components/debug/debug.scss diff --git a/x-pack/plugins/canvas/public/components/debug/debug.tsx b/src/plugins/expression_error/public/components/debug/debug.tsx similarity index 66% rename from x-pack/plugins/canvas/public/components/debug/debug.tsx rename to src/plugins/expression_error/public/components/debug/debug.tsx index 8dc09a32415a5..925616d5550b1 100644 --- a/x-pack/plugins/canvas/public/components/debug/debug.tsx +++ b/src/plugins/expression_error/public/components/debug/debug.tsx @@ -1,13 +1,14 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ import React from 'react'; -import PropTypes from 'prop-types'; import { EuiCode } from '@elastic/eui'; +import './debug.scss'; const LimitRows = (key: string, value: any) => { if (key === 'rows') { @@ -16,14 +17,10 @@ const LimitRows = (key: string, value: any) => { return value; }; -export const Debug = ({ payload }: any) => ( +export const Debug = ({ payload }: { payload: unknown }) => (
       {JSON.stringify(payload, LimitRows, 2)}
     
); - -Debug.propTypes = { - payload: PropTypes.object, -}; diff --git a/src/plugins/expression_error/public/components/debug/index.tsx b/src/plugins/expression_error/public/components/debug/index.tsx new file mode 100644 index 0000000000000..1984eecfe4e39 --- /dev/null +++ b/src/plugins/expression_error/public/components/debug/index.tsx @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +// eslint-disable-next-line import/no-default-export +export { Debug as default } from './debug'; diff --git a/src/plugins/expression_error/public/components/debug_component.tsx b/src/plugins/expression_error/public/components/debug_component.tsx new file mode 100644 index 0000000000000..6cb927380d669 --- /dev/null +++ b/src/plugins/expression_error/public/components/debug_component.tsx @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useState, useEffect, useCallback } from 'react'; +import { useResizeObserver } from '@elastic/eui'; +import { IInterpreterRenderHandlers } from '../../../expressions'; +import { withSuspense } from '../../../presentation_util/public'; +import { NodeDimensions } from '../../common/types'; +import { LazyDebugComponent } from '.'; + +const Debug = withSuspense(LazyDebugComponent); + +interface DebugComponentProps { + onLoaded: IInterpreterRenderHandlers['done']; + parentNode: HTMLElement; + payload: any; +} + +function DebugComponent({ onLoaded, parentNode, payload }: DebugComponentProps) { + const parentNodeDimensions = useResizeObserver(parentNode); + const [dimensions, setDimensions] = useState({ + width: parentNode.offsetWidth, + height: parentNode.offsetHeight, + }); + + const updateDebugView = useCallback(() => { + setDimensions({ + width: parentNode.offsetWidth, + height: parentNode.offsetHeight, + }); + onLoaded(); + }, [parentNode, onLoaded]); + + useEffect(() => { + updateDebugView(); + }, [parentNodeDimensions, updateDebugView]); + + return ( +
+ +
+ ); +} + +// default export required for React.Lazy +// eslint-disable-next-line import/no-default-export +export { DebugComponent as default }; diff --git a/x-pack/plugins/canvas/public/components/error/error.tsx b/src/plugins/expression_error/public/components/error/error.tsx similarity index 76% rename from x-pack/plugins/canvas/public/components/error/error.tsx rename to src/plugins/expression_error/public/components/error/error.tsx index cb2c2cd5d58c1..99318357d8602 100644 --- a/x-pack/plugins/canvas/public/components/error/error.tsx +++ b/src/plugins/expression_error/public/components/error/error.tsx @@ -1,28 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ import React, { FC } from 'react'; -import PropTypes from 'prop-types'; import { EuiCallOut } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { get } from 'lodash'; - import { ShowDebugging } from './show_debugging'; const strings = { getDescription: () => - i18n.translate('xpack.canvas.errorComponent.description', { + i18n.translate('expressionError.errorComponent.description', { defaultMessage: 'Expression failed with the message:', }), getTitle: () => - i18n.translate('xpack.canvas.errorComponent.title', { + i18n.translate('expressionError.errorComponent.title', { defaultMessage: 'Whoops! Expression failed', }), }; + export interface Props { payload: { error: Error; @@ -46,7 +46,3 @@ export const Error: FC = ({ payload }) => { ); }; - -Error.propTypes = { - payload: PropTypes.object.isRequired, -}; diff --git a/src/plugins/expression_error/public/components/error/index.ts b/src/plugins/expression_error/public/components/error/index.ts new file mode 100644 index 0000000000000..4edaa4260d1ce --- /dev/null +++ b/src/plugins/expression_error/public/components/error/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +// eslint-disable-next-line import/no-default-export +export { Error as default } from './error'; diff --git a/x-pack/plugins/canvas/public/components/error/show_debugging.tsx b/src/plugins/expression_error/public/components/error/show_debugging.tsx similarity index 77% rename from x-pack/plugins/canvas/public/components/error/show_debugging.tsx rename to src/plugins/expression_error/public/components/error/show_debugging.tsx index 844bd9fdbff6e..5ce3f79a6139b 100644 --- a/x-pack/plugins/canvas/public/components/error/show_debugging.tsx +++ b/src/plugins/expression_error/public/components/error/show_debugging.tsx @@ -1,14 +1,14 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ import React, { FC, useState } from 'react'; -import PropTypes from 'prop-types'; import { EuiButtonEmpty } from '@elastic/eui'; -import { Debug } from '../debug'; +import Debug from '../debug'; import { Props } from './error'; export const ShowDebugging: FC = ({ payload }) => { @@ -30,7 +30,3 @@ export const ShowDebugging: FC = ({ payload }) => { ); }; - -ShowDebugging.propTypes = { - payload: PropTypes.object.isRequired, -}; diff --git a/src/plugins/expression_error/public/components/error_component.tsx b/src/plugins/expression_error/public/components/error_component.tsx new file mode 100644 index 0000000000000..58161d8a068a2 --- /dev/null +++ b/src/plugins/expression_error/public/components/error_component.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useState, useEffect, useCallback } from 'react'; +import { EuiIcon, useResizeObserver, EuiPopover } from '@elastic/eui'; +import { IInterpreterRenderHandlers } from '../../../expressions'; +import { withSuspense } from '../../../presentation_util/public'; +import { ErrorRendererConfig } from '../../common/types'; +import { LazyErrorComponent } from '.'; + +const Error = withSuspense(LazyErrorComponent); + +interface ErrorComponentProps extends ErrorRendererConfig { + onLoaded: IInterpreterRenderHandlers['done']; + parentNode: HTMLElement; +} + +function ErrorComponent({ onLoaded, parentNode, error }: ErrorComponentProps) { + const getButtonSize = (node: HTMLElement) => Math.min(node.clientHeight, node.clientWidth); + const parentNodeDimensions = useResizeObserver(parentNode); + + const [buttonSize, setButtonSize] = useState(getButtonSize(parentNode)); + const [isPopoverOpen, setPopoverOpen] = useState(false); + + const handlePopoverClick = () => setPopoverOpen(!isPopoverOpen); + const closePopover = () => setPopoverOpen(false); + + const updateErrorView = useCallback(() => { + setButtonSize(getButtonSize(parentNode)); + onLoaded(); + }, [parentNode, onLoaded]); + + useEffect(() => { + updateErrorView(); + }, [parentNodeDimensions, updateErrorView]); + + return ( +
+ + } + isOpen={isPopoverOpen} + > + + +
+ ); +} + +// default export required for React.Lazy +// eslint-disable-next-line import/no-default-export +export { ErrorComponent as default }; diff --git a/src/plugins/expression_error/public/components/index.ts b/src/plugins/expression_error/public/components/index.ts new file mode 100644 index 0000000000000..23eb02fa063a7 --- /dev/null +++ b/src/plugins/expression_error/public/components/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { lazy } from 'react'; + +export const LazyErrorComponent = lazy(() => import('./error')); +export const LazyDebugComponent = lazy(() => import('./debug')); + +export const LazyErrorRenderComponent = lazy(() => import('./error_component')); +export const LazyDebugRenderComponent = lazy(() => import('./debug_component')); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/error/__stories__/__snapshots__/error.stories.storyshot b/src/plugins/expression_error/public/expression_renderers/__stories__/__snapshots__/error.stories.storyshot similarity index 100% rename from x-pack/plugins/canvas/canvas_plugin_src/renderers/error/__stories__/__snapshots__/error.stories.storyshot rename to src/plugins/expression_error/public/expression_renderers/__stories__/__snapshots__/error.stories.storyshot diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/error/__stories__/error.stories.tsx b/src/plugins/expression_error/public/expression_renderers/__stories__/error_renderer.stories.tsx similarity index 51% rename from x-pack/plugins/canvas/canvas_plugin_src/renderers/error/__stories__/error.stories.tsx rename to src/plugins/expression_error/public/expression_renderers/__stories__/error_renderer.stories.tsx index 9598fa00e4e35..9081a8512c11a 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/error/__stories__/error.stories.tsx +++ b/src/plugins/expression_error/public/expression_renderers/__stories__/error_renderer.stories.tsx @@ -1,19 +1,20 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ import React from 'react'; import { storiesOf } from '@storybook/react'; -import { error } from '../'; -import { Render } from '../../__stories__/render'; +import { errorRenderer } from '../error_renderer'; +import { Render } from '../../../../presentation_util/public/__stories__'; storiesOf('renderers/error', module).add('default', () => { const thrownError = new Error('There was an error'); const config = { error: thrownError, }; - return ; + return ; }); diff --git a/src/plugins/expression_error/public/expression_renderers/debug_renderer.tsx b/src/plugins/expression_error/public/expression_renderers/debug_renderer.tsx new file mode 100644 index 0000000000000..e3cf86b67148f --- /dev/null +++ b/src/plugins/expression_error/public/expression_renderers/debug_renderer.tsx @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { render, unmountComponentAtNode } from 'react-dom'; +import React from 'react'; +import { ExpressionRenderDefinition } from 'src/plugins/expressions/common'; +import { i18n } from '@kbn/i18n'; +import { withSuspense } from '../../../../../src/plugins/presentation_util/public'; +import { LazyDebugRenderComponent } from '../components'; +import { JSON } from '../../common'; + +const Debug = withSuspense(LazyDebugRenderComponent); + +const strings = { + getDisplayName: () => + i18n.translate('expressionError.renderer.debug.displayName', { + defaultMessage: 'Debug', + }), + getHelpDescription: () => + i18n.translate('expressionError.renderer.debug.helpDescription', { + defaultMessage: 'Render debug output as formatted {JSON}', + values: { + JSON, + }, + }), +}; + +export const debugRenderer = (): ExpressionRenderDefinition => ({ + name: 'debug', + displayName: strings.getDisplayName(), + help: strings.getHelpDescription(), + reuseDomNode: true, + render(domNode, config, handlers) { + handlers.onDestroy(() => unmountComponentAtNode(domNode)); + render(, domNode); + }, +}); diff --git a/src/plugins/expression_error/public/expression_renderers/error_renderer.tsx b/src/plugins/expression_error/public/expression_renderers/error_renderer.tsx new file mode 100644 index 0000000000000..8ce4d5fdbbbca --- /dev/null +++ b/src/plugins/expression_error/public/expression_renderers/error_renderer.tsx @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import React from 'react'; +import { render, unmountComponentAtNode } from 'react-dom'; +import { I18nProvider } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; +import { ExpressionRenderDefinition, IInterpreterRenderHandlers } from 'src/plugins/expressions'; +import { withSuspense } from '../../../presentation_util/public'; +import { ErrorRendererConfig } from '../../common/types'; +import { LazyErrorRenderComponent } from '../components'; + +const errorStrings = { + getDisplayName: () => + i18n.translate('expressionError.renderer.error.displayName', { + defaultMessage: 'Error information', + }), + getHelpDescription: () => + i18n.translate('expressionError.renderer.error.helpDescription', { + defaultMessage: 'Render error data in a way that is helpful to users', + }), +}; + +const ErrorComponent = withSuspense(LazyErrorRenderComponent); + +export const errorRenderer = (): ExpressionRenderDefinition => ({ + name: 'error', + displayName: errorStrings.getDisplayName(), + help: errorStrings.getHelpDescription(), + reuseDomNode: true, + render: async ( + domNode: HTMLElement, + config: ErrorRendererConfig, + handlers: IInterpreterRenderHandlers + ) => { + handlers.onDestroy(() => { + unmountComponentAtNode(domNode); + }); + + render( + + + , + domNode + ); + }, +}); diff --git a/src/plugins/expression_error/public/expression_renderers/index.ts b/src/plugins/expression_error/public/expression_renderers/index.ts new file mode 100644 index 0000000000000..237ee5644cdc0 --- /dev/null +++ b/src/plugins/expression_error/public/expression_renderers/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { errorRenderer } from './error_renderer'; +import { debugRenderer } from './debug_renderer'; + +export const renderers = [errorRenderer, debugRenderer]; + +export { errorRenderer, debugRenderer }; diff --git a/src/plugins/expression_error/public/index.ts b/src/plugins/expression_error/public/index.ts new file mode 100755 index 0000000000000..04c29a96b853a --- /dev/null +++ b/src/plugins/expression_error/public/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ExpressionErrorPlugin } from './plugin'; + +export type { ExpressionErrorPluginSetup, ExpressionErrorPluginStart } from './plugin'; + +export function plugin() { + return new ExpressionErrorPlugin(); +} + +export * from './expression_renderers'; +export { LazyDebugComponent, LazyErrorComponent } from './components'; diff --git a/src/plugins/expression_error/public/plugin.ts b/src/plugins/expression_error/public/plugin.ts new file mode 100755 index 0000000000000..3727cab5436c9 --- /dev/null +++ b/src/plugins/expression_error/public/plugin.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { CoreSetup, CoreStart, Plugin } from '../../../core/public'; +import { ExpressionsStart, ExpressionsSetup } from '../../expressions/public'; +import { errorRenderer, debugRenderer } from './expression_renderers'; + +interface SetupDeps { + expressions: ExpressionsSetup; +} + +interface StartDeps { + expression: ExpressionsStart; +} + +export type ExpressionErrorPluginSetup = void; +export type ExpressionErrorPluginStart = void; + +export class ExpressionErrorPlugin + implements Plugin { + public setup(core: CoreSetup, { expressions }: SetupDeps): ExpressionErrorPluginSetup { + expressions.registerRenderer(errorRenderer); + expressions.registerRenderer(debugRenderer); + } + + public start(core: CoreStart): ExpressionErrorPluginStart {} + + public stop() {} +} diff --git a/src/plugins/expression_error/tsconfig.json b/src/plugins/expression_error/tsconfig.json new file mode 100644 index 0000000000000..aa4562ec73576 --- /dev/null +++ b/src/plugins/expression_error/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true, + "isolatedModules": true + }, + "include": [ + "common/**/*", + "public/**/*", + "server/**/*", + ], + "references": [ + { "path": "../../core/tsconfig.json" }, + { "path": "../presentation_util/tsconfig.json" }, + { "path": "../expressions/tsconfig.json" }, + ] +} diff --git a/src/plugins/expressions/common/execution/execution.test.ts b/src/plugins/expressions/common/execution/execution.test.ts index 8c6f457105d42..2e9d4b91908a0 100644 --- a/src/plugins/expressions/common/execution/execution.test.ts +++ b/src/plugins/expressions/common/execution/execution.test.ts @@ -339,6 +339,14 @@ describe('Execution', () => { }); expect(execution.inspectorAdapters).toBe(inspectorAdapters); }); + + test('it should reset the request adapter only on startup', async () => { + const inspectorAdapters = { requests: { reset: jest.fn() } }; + await run('add val={add 5 | access "value"}', { + inspectorAdapters, + }); + expect(inspectorAdapters.requests.reset).toHaveBeenCalledTimes(1); + }); }); describe('expression abortion', () => { diff --git a/src/plugins/expressions/common/execution/execution.ts b/src/plugins/expressions/common/execution/execution.ts index 47209c348257e..ef925b3a68294 100644 --- a/src/plugins/expressions/common/execution/execution.ts +++ b/src/plugins/expressions/common/execution/execution.ts @@ -280,13 +280,18 @@ export class Execution< * because in legacy interpreter it was set to `null` by default. */ public start( - input: Input = null as any + input: Input = null as any, + isSubExpression?: boolean ): Observable> { if (this.hasStarted) throw new Error('Execution already started.'); this.hasStarted = true; this.input = input; this.state.transitions.start(); + if (!isSubExpression) { + this.context.inspectorAdapters.requests?.reset(); + } + if (isObservable(input)) { input.subscribe(this.input$); } else if (isPromise(input)) { @@ -534,7 +539,7 @@ export class Execution< ); this.childExecutions.push(execution); - return execution.start(input); + return execution.start(input, true); case 'string': case 'number': case 'null': diff --git a/src/plugins/expressions/public/public.api.md b/src/plugins/expressions/public/public.api.md index 2d9c6d94cfa6d..55655cfc5d156 100644 --- a/src/plugins/expressions/public/public.api.md +++ b/src/plugins/expressions/public/public.api.md @@ -120,7 +120,7 @@ export class Execution; readonly result: Observable>; - start(input?: Input): Observable>; + start(input?: Input, isSubExpression?: boolean): Observable>; // Warning: (ae-forgotten-export) The symbol "ExecutionResult" needs to be exported by the entry point index.d.ts readonly state: ExecutionContainer>; } diff --git a/src/plugins/expressions/server/server.api.md b/src/plugins/expressions/server/server.api.md index ec16d95ea8a3f..a34727e7a770f 100644 --- a/src/plugins/expressions/server/server.api.md +++ b/src/plugins/expressions/server/server.api.md @@ -118,7 +118,7 @@ export class Execution; readonly result: Observable>; - start(input?: Input): Observable>; + start(input?: Input, isSubExpression?: boolean): Observable>; // Warning: (ae-forgotten-export) The symbol "ExecutionResult" needs to be exported by the entry point index.d.ts readonly state: ExecutionContainer>; } diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap index 32a2b936edd00..c03899554ffff 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap @@ -1,5 +1,148 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`IndexedFieldsTable IndexedFieldsTable with rollup index pattern should render normally 1`] = ` +
+ + +`; + exports[`IndexedFieldsTable should filter based on the query bar 1`] = `
diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx index b9957534d8c69..163152b52e80b 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx @@ -8,13 +8,13 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { IIndexPattern } from 'src/plugins/data/public'; +import { IndexPattern } from 'src/plugins/data/public'; import { IndexedFieldItem } from '../../types'; import { Table, renderFieldName } from './table'; const indexPattern = { timeFieldName: 'timestamp', -} as IIndexPattern; +} as IndexPattern; const items: IndexedFieldItem[] = [ { diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx index e587ada6695cb..6d37e8f13d6b3 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx @@ -8,8 +8,9 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { IndexPatternField, IndexPattern } from 'src/plugins/data/public'; +import { IndexPatternField, IndexPattern, IndexPatternType } from 'src/plugins/data/public'; import { IndexedFieldsTable } from './indexed_fields_table'; +import { RollupIndexPatternListConfig } from '../../../service/list'; jest.mock('@elastic/eui', () => ({ EuiFlexGroup: 'eui-flex-group', @@ -28,7 +29,8 @@ jest.mock('./components/table', () => ({ const helpers = { editField: (fieldName: string) => {}, deleteField: (fieldName: string) => {}, - getFieldInfo: () => [], + // getFieldInfo handles non rollups as well + getFieldInfo: new RollupIndexPatternListConfig().getFieldInfo, }; const indexPattern = ({ @@ -36,6 +38,34 @@ const indexPattern = ({ getFormatterForFieldNoDefault: () => ({ params: () => ({}) }), } as unknown) as IndexPattern; +const rollupIndexPattern = ({ + type: IndexPatternType.ROLLUP, + typeMeta: { + params: { + 'rollup-index': 'rollup', + }, + aggs: { + date_histogram: { + timestamp: { + agg: 'date_histogram', + fixed_interval: '30s', + delay: '30s', + time_zone: 'UTC', + }, + }, + terms: { Elastic: { agg: 'terms' } }, + histogram: { amount: { agg: 'histogram', interval: 5 } }, + avg: { amount: { agg: 'avg' } }, + max: { amount: { agg: 'max' } }, + min: { amount: { agg: 'min' } }, + sum: { amount: { agg: 'sum' } }, + value_count: { amount: { agg: 'value_count' } }, + }, + }, + getNonScriptedFields: () => fields, + getFormatterForFieldNoDefault: () => ({ params: () => ({}) }), +} as unknown) as IndexPattern; + const mockFieldToIndexPatternField = ( spec: Record ) => { @@ -51,6 +81,7 @@ const fields = [ }, { name: 'timestamp', displayName: 'timestamp', esTypes: ['date'] }, { name: 'conflictingField', displayName: 'conflictingField', esTypes: ['keyword', 'long'] }, + { name: 'amount', displayName: 'amount', esTypes: ['long'] }, ].map(mockFieldToIndexPatternField); describe('IndexedFieldsTable', () => { @@ -115,4 +146,26 @@ describe('IndexedFieldsTable', () => { expect(component).toMatchSnapshot(); }); + + describe('IndexedFieldsTable with rollup index pattern', () => { + test('should render normally', async () => { + const component = shallow( + { + return () => false; + }} + indexedFieldTypeFilter="" + fieldFilter="" + /> + ); + + await new Promise((resolve) => process.nextTick(resolve)); + component.update(); + + expect(component).toMatchSnapshot(); + }); + }); }); diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx index ee1147997079f..4e9aeb1874c89 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx @@ -8,7 +8,7 @@ import React, { Component } from 'react'; import { createSelector } from 'reselect'; -import { IndexPatternField, IndexPattern, IFieldType } from '../../../../../../plugins/data/public'; +import { IndexPatternField, IndexPattern } from '../../../../../../plugins/data/public'; import { Table } from './components/table'; import { IndexedFieldItem } from './types'; @@ -20,7 +20,7 @@ interface IndexedFieldsTableProps { helpers: { editField: (fieldName: string) => void; deleteField: (fieldName: string) => void; - getFieldInfo: (indexPattern: IndexPattern, field: IFieldType) => string[]; + getFieldInfo: (indexPattern: IndexPattern, field: IndexPatternField) => string[]; }; fieldWildcardMatcher: (filters: any[]) => (val: any) => boolean; } diff --git a/src/plugins/index_pattern_management/public/service/list/config.ts b/src/plugins/index_pattern_management/public/service/list/config.ts index e13f8c1c06241..4be27fc47d0db 100644 --- a/src/plugins/index_pattern_management/public/service/list/config.ts +++ b/src/plugins/index_pattern_management/public/service/list/config.ts @@ -7,8 +7,7 @@ */ import { i18n } from '@kbn/i18n'; -import { IIndexPattern, IFieldType } from 'src/plugins/data/public'; -import { SimpleSavedObject } from 'src/core/public'; +import { IndexPattern, IndexPatternField, IndexPatternType } from '../../../../data/public'; export interface IndexPatternTag { key: string; @@ -23,12 +22,9 @@ const defaultIndexPatternListName = i18n.translate( ); export class IndexPatternListConfig { - public readonly key = 'default'; + public readonly key: IndexPatternType = IndexPatternType.DEFAULT; - public getIndexPatternTags( - indexPattern: IIndexPattern | SimpleSavedObject, - isDefault: boolean - ): IndexPatternTag[] { + public getIndexPatternTags(indexPattern: IndexPattern, isDefault: boolean): IndexPatternTag[] { return isDefault ? [ { @@ -39,11 +35,11 @@ export class IndexPatternListConfig { : []; } - public getFieldInfo(indexPattern: IIndexPattern, field: IFieldType): string[] { + public getFieldInfo(indexPattern: IndexPattern, field: IndexPatternField): string[] { return []; } - public areScriptedFieldsEnabled(indexPattern: IIndexPattern): boolean { + public areScriptedFieldsEnabled(indexPattern: IndexPattern): boolean { return true; } } diff --git a/src/plugins/index_pattern_management/public/service/list/manager.ts b/src/plugins/index_pattern_management/public/service/list/manager.ts index bdb2d47057f1f..d9cefbd8001a5 100644 --- a/src/plugins/index_pattern_management/public/service/list/manager.ts +++ b/src/plugins/index_pattern_management/public/service/list/manager.ts @@ -6,13 +6,11 @@ * Side Public License, v 1. */ -import { IIndexPattern, IFieldType } from 'src/plugins/data/public'; -import { SimpleSavedObject } from 'src/core/public'; +import { IndexPattern, IndexPatternField } from 'src/plugins/data/public'; import { once } from 'lodash'; import { CoreStart } from '../../../../../core/public'; import { IndexPatternListConfig, IndexPatternTag } from './config'; import { CONFIG_ROLLUPS } from '../../constants'; -// @ts-ignore import { RollupIndexPatternListConfig } from './rollup_list_config'; interface IndexPatternListManagerStart { @@ -32,10 +30,7 @@ export class IndexPatternListManager { return configs; }); return { - getIndexPatternTags: ( - indexPattern: IIndexPattern | SimpleSavedObject, - isDefault: boolean - ) => + getIndexPatternTags: (indexPattern: IndexPattern, isDefault: boolean) => getConfigs().reduce( (tags: IndexPatternTag[], config) => config.getIndexPatternTags @@ -44,14 +39,14 @@ export class IndexPatternListManager { [] ), - getFieldInfo: (indexPattern: IIndexPattern, field: IFieldType): string[] => + getFieldInfo: (indexPattern: IndexPattern, field: IndexPatternField): string[] => getConfigs().reduce( (info: string[], config) => config.getFieldInfo ? info.concat(config.getFieldInfo(indexPattern, field)) : info, [] ), - areScriptedFieldsEnabled: (indexPattern: IIndexPattern): boolean => + areScriptedFieldsEnabled: (indexPattern: IndexPattern): boolean => getConfigs().every((config) => config.areScriptedFieldsEnabled ? config.areScriptedFieldsEnabled(indexPattern) : true ), diff --git a/src/plugins/index_pattern_management/public/service/list/rollup_list_config.js b/src/plugins/index_pattern_management/public/service/list/rollup_list_config.ts similarity index 69% rename from src/plugins/index_pattern_management/public/service/list/rollup_list_config.js rename to src/plugins/index_pattern_management/public/service/list/rollup_list_config.ts index 9a80d5fd0d622..bb9da0d461701 100644 --- a/src/plugins/index_pattern_management/public/service/list/rollup_list_config.js +++ b/src/plugins/index_pattern_management/public/service/list/rollup_list_config.ts @@ -6,18 +6,17 @@ * Side Public License, v 1. */ +import { IndexPattern, IndexPatternField, IndexPatternType } from '../../../../data/public'; import { IndexPatternListConfig } from '.'; -function isRollup(indexPattern) { - return ( - indexPattern.type === 'rollup' || (indexPattern.get && indexPattern.get('type') === 'rollup') - ); +function isRollup(indexPattern: IndexPattern) { + return indexPattern.type === IndexPatternType.ROLLUP; } export class RollupIndexPatternListConfig extends IndexPatternListConfig { - key = 'rollup'; + key = IndexPatternType.ROLLUP; - getIndexPatternTags = (indexPattern) => { + getIndexPatternTags = (indexPattern: IndexPattern) => { return isRollup(indexPattern) ? [ { @@ -28,13 +27,13 @@ export class RollupIndexPatternListConfig extends IndexPatternListConfig { : []; }; - getFieldInfo = (indexPattern, field) => { + getFieldInfo = (indexPattern: IndexPattern, field: IndexPatternField) => { if (!isRollup(indexPattern)) { return []; } const allAggs = indexPattern.typeMeta && indexPattern.typeMeta.aggs; - const fieldAggs = allAggs && Object.keys(allAggs).filter((agg) => allAggs[agg][field]); + const fieldAggs = allAggs && Object.keys(allAggs).filter((agg) => allAggs[agg][field.name]); if (!fieldAggs || !fieldAggs.length) { return []; @@ -42,13 +41,12 @@ export class RollupIndexPatternListConfig extends IndexPatternListConfig { return ['Rollup aggregations:'].concat( fieldAggs.map((aggName) => { - const agg = allAggs[aggName][field]; + const agg = allAggs![aggName][field.name]; switch (aggName) { case 'date_histogram': - return `${aggName} (interval: ${agg.interval}, ${ + return `${aggName} (interval: ${agg.fixed_interval}, ${ agg.delay ? `delay: ${agg.delay},` : '' } ${agg.time_zone})`; - break; case 'histogram': return `${aggName} (interval: ${agg.interval})`; default: @@ -58,7 +56,7 @@ export class RollupIndexPatternListConfig extends IndexPatternListConfig { ); }; - areScriptedFieldsEnabled = (indexPattern) => { + areScriptedFieldsEnabled = (indexPattern: IndexPattern) => { return !isRollup(indexPattern); }; } diff --git a/src/plugins/kibana_react/public/page_template/page_template.test.tsx b/src/plugins/kibana_react/public/page_template/page_template.test.tsx index 43f96a6c2b98c..2fdedce23b09b 100644 --- a/src/plugins/kibana_react/public/page_template/page_template.test.tsx +++ b/src/plugins/kibana_react/public/page_template/page_template.test.tsx @@ -125,4 +125,20 @@ describe('KibanaPageTemplate', () => { ); expect(component).toMatchSnapshot(); }); + + test('render sidebar classes', () => { + const component = shallow( + + ); + expect(component.prop('pageSideBarProps').className).toEqual( + 'kbnPageTemplate__pageSideBar customClass' + ); + }); }); diff --git a/src/plugins/kibana_react/public/page_template/page_template.tsx b/src/plugins/kibana_react/public/page_template/page_template.tsx index dabec978b67a4..1e63f2e068508 100644 --- a/src/plugins/kibana_react/public/page_template/page_template.tsx +++ b/src/plugins/kibana_react/public/page_template/page_template.tsx @@ -48,6 +48,7 @@ export const KibanaPageTemplate: FunctionComponent = ({ isEmptyState, restrictWidth = true, pageSideBar, + pageSideBarProps, solutionNav, ...rest }) => { @@ -117,8 +118,8 @@ export const KibanaPageTemplate: FunctionComponent = ({ pageSideBar={pageSideBar} pageSideBarProps={{ paddingSize: solutionNav ? 'none' : 'l', - ...rest.pageSideBarProps, - className: classNames(sideBarClasses, rest.pageSideBarProps?.className), + ...pageSideBarProps, + className: classNames(sideBarClasses, pageSideBarProps?.className), }} {...rest} > diff --git a/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx b/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx index da01f9d44879b..82757e9a8e357 100644 --- a/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx +++ b/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx @@ -449,7 +449,9 @@ export const getTopNavConfig = ( onSave={onSave} options={tagOptions} getAppNameFromId={stateTransfer.getAppNameFromId} - objectType={'visualization'} + objectType={i18n.translate('visualize.topNavMenu.saveVisualizationObjectType', { + defaultMessage: 'visualization', + })} onClose={() => {}} originatingApp={originatingApp} returnToOriginSwitchLabel={ @@ -475,7 +477,9 @@ export const getTopNavConfig = ( canSaveByReference={Boolean(visualizeCapabilities.save)} onSave={onSave} tagOptions={tagOptions} - objectType={'visualization'} + objectType={i18n.translate('visualize.topNavMenu.saveVisualizationObjectType', { + defaultMessage: 'visualization', + })} onClose={() => {}} /> ); diff --git a/test/functional/apps/context/_context_navigation.js b/test/functional/apps/context/_context_navigation.js index 2efc145b12561..7f72d44c50ea0 100644 --- a/test/functional/apps/context/_context_navigation.js +++ b/test/functional/apps/context/_context_navigation.js @@ -21,8 +21,7 @@ export default function ({ getService, getPageObjects }) { const PageObjects = getPageObjects(['common', 'context', 'discover', 'timePicker']); const kibanaServer = getService('kibanaServer'); - // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/104364 - describe.skip('discover - context - back navigation', function contextSize() { + describe('discover - context - back navigation', function contextSize() { before(async function () { await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); await kibanaServer.uiSettings.update({ 'doc_table:legacy': true }); diff --git a/test/functional/apps/context/_discover_navigation.js b/test/functional/apps/context/_discover_navigation.js index 6a2298ba48cb4..a09be8b35ba8f 100644 --- a/test/functional/apps/context/_discover_navigation.js +++ b/test/functional/apps/context/_discover_navigation.js @@ -32,8 +32,7 @@ export default function ({ getService, getPageObjects }) { const browser = getService('browser'); const kibanaServer = getService('kibanaServer'); - // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/104413 - describe.skip('context link in discover', () => { + describe('context link in discover', () => { before(async () => { await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); await kibanaServer.uiSettings.update({ diff --git a/test/functional/apps/dashboard/saved_search_embeddable.ts b/test/functional/apps/dashboard/saved_search_embeddable.ts index 33d015a4c6019..5bcec338aad1e 100644 --- a/test/functional/apps/dashboard/saved_search_embeddable.ts +++ b/test/functional/apps/dashboard/saved_search_embeddable.ts @@ -17,8 +17,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const kibanaServer = getService('kibanaServer'); const PageObjects = getPageObjects(['common', 'dashboard', 'header', 'timePicker', 'discover']); - // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/104365 - describe.skip('dashboard saved search embeddable', () => { + describe('dashboard saved search embeddable', () => { before(async () => { await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/dashboard/current/data'); diff --git a/test/functional/apps/dashboard/view_edit.ts b/test/functional/apps/dashboard/view_edit.ts index 1ca70112c3d1e..b29b07f9df4e4 100644 --- a/test/functional/apps/dashboard/view_edit.ts +++ b/test/functional/apps/dashboard/view_edit.ts @@ -19,8 +19,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const dashboardName = 'dashboard with filter'; const filterBar = getService('filterBar'); - // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/104467 - describe.skip('dashboard view edit mode', function viewEditModeTests() { + describe('dashboard view edit mode', function viewEditModeTests() { before(async () => { await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); await kibanaServer.uiSettings.replace({ diff --git a/test/functional/apps/discover/_date_nested.ts b/test/functional/apps/discover/_date_nested.ts new file mode 100644 index 0000000000000..8297d84832ff6 --- /dev/null +++ b/test/functional/apps/discover/_date_nested.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['common', 'timePicker', 'discover']); + const security = getService('security'); + + describe('timefield is a date in a nested field', function () { + before(async function () { + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/date_nested'); + await security.testUser.setRoles(['kibana_admin', 'kibana_date_nested']); + await PageObjects.common.navigateToApp('discover'); + }); + + after(async function unloadMakelogs() { + await security.testUser.restoreDefaults(); + await esArchiver.unload('test/functional/fixtures/es_archiver/date_nested'); + }); + + it('should show an error message', async function () { + await PageObjects.discover.selectIndexPattern('date-nested'); + await PageObjects.discover.waitUntilSearchingHasFinished(); + await testSubjects.existOrFail('discoverNoResultsError'); + }); + }); +} diff --git a/test/functional/apps/discover/_discover.ts b/test/functional/apps/discover/_discover.ts index 245b895d75b3a..bb75b4441f880 100644 --- a/test/functional/apps/discover/_discover.ts +++ b/test/functional/apps/discover/_discover.ts @@ -38,8 +38,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.timePicker.setDefaultAbsoluteRange(); }); - // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/104409 - describe.skip('query', function () { + describe('query', function () { const queryName1 = 'Query # 1'; it('should show correct time range string by timepicker', async function () { diff --git a/test/functional/apps/discover/_field_data.ts b/test/functional/apps/discover/_field_data.ts index ec9f9cf65e0fa..338d17ba31ff4 100644 --- a/test/functional/apps/discover/_field_data.ts +++ b/test/functional/apps/discover/_field_data.ts @@ -33,7 +33,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); await PageObjects.common.navigateToApp('discover'); }); - describe('field data', function () { it('search php should show the correct hit count', async function () { const expectedHitCount = '445'; diff --git a/test/functional/apps/discover/_saved_queries.ts b/test/functional/apps/discover/_saved_queries.ts index 29073c5fe4ebb..20f2cab907d9b 100644 --- a/test/functional/apps/discover/_saved_queries.ts +++ b/test/functional/apps/discover/_saved_queries.ts @@ -40,8 +40,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.timePicker.setDefaultAbsoluteRange(); }); - // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/104366 - describe.skip('saved query management component functionality', function () { + describe('saved query management component functionality', function () { before(async function () { // set up a query with filters and a time filter log.debug('set up a query with filters to save'); diff --git a/test/functional/apps/discover/index.ts b/test/functional/apps/discover/index.ts index a17bf53e7f478..3a18a55fe138b 100644 --- a/test/functional/apps/discover/index.ts +++ b/test/functional/apps/discover/index.ts @@ -12,8 +12,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const browser = getService('browser'); - // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/104466 - describe.skip('discover app', function () { + describe('discover app', function () { this.tags('ciGroup6'); before(function () { @@ -51,5 +50,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./_indexpattern_with_unmapped_fields')); loadTestFile(require.resolve('./_runtime_fields_editor')); loadTestFile(require.resolve('./_huge_fields')); + loadTestFile(require.resolve('./_date_nested')); }); } diff --git a/test/functional/config.js b/test/functional/config.js index c2c856517c58e..1c0c519f21e4c 100644 --- a/test/functional/config.js +++ b/test/functional/config.js @@ -247,6 +247,21 @@ export default async function ({ readConfigFile }) { }, kibana: [], }, + + kibana_date_nested: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['date-nested'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, kibana_message_with_newline: { elasticsearch: { cluster: [], diff --git a/test/functional/fixtures/es_archiver/date_nested/data.json b/test/functional/fixtures/es_archiver/date_nested/data.json new file mode 100644 index 0000000000000..0bdb3fc510a63 --- /dev/null +++ b/test/functional/fixtures/es_archiver/date_nested/data.json @@ -0,0 +1,30 @@ +{ + "type": "doc", + "value": { + "id": "index-pattern:date-nested", + "index": ".kibana", + "source": { + "index-pattern": { + "fields":"[]", + "timeFieldName": "@timestamp", + "title": "date-nested" + }, + "type": "index-pattern" + } + } +} + + +{ + "type": "doc", + "value": { + "id": "date-nested-1", + "index": "date-nested", + "source": { + "message" : "test", + "nested": { + "timestamp": "2021-06-30T12:00:00.123Z" + } + } + } +} diff --git a/test/functional/fixtures/es_archiver/date_nested/mappings.json b/test/functional/fixtures/es_archiver/date_nested/mappings.json new file mode 100644 index 0000000000000..f30e5863f4f8b --- /dev/null +++ b/test/functional/fixtures/es_archiver/date_nested/mappings.json @@ -0,0 +1,22 @@ +{ + "type": "index", + "value": { + "index": "date-nested", + "mappings": { + "properties": { + "message": { + "type": "text" + }, + "nested": { + "type": "nested" + } + } + }, + "settings": { + "index": { + "number_of_replicas": "0", + "number_of_shards": "1" + } + } + } +} diff --git a/tsconfig.json b/tsconfig.json index f6df8fcbb6406..082325306e379 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -76,7 +76,6 @@ { "path": "./x-pack/plugins/canvas/tsconfig.json" }, { "path": "./x-pack/plugins/cases/tsconfig.json" }, { "path": "./x-pack/plugins/cloud/tsconfig.json" }, - { "path": "./x-pack/plugins/console_extensions/tsconfig.json" }, { "path": "./x-pack/plugins/data_enhanced/tsconfig.json" }, { "path": "./x-pack/plugins/dashboard_mode/tsconfig.json" }, { "path": "./x-pack/plugins/discover_enhanced/tsconfig.json" }, diff --git a/tsconfig.refs.json b/tsconfig.refs.json index e08b50cc055c1..bbaf18d29ae2a 100644 --- a/tsconfig.refs.json +++ b/tsconfig.refs.json @@ -63,7 +63,6 @@ { "path": "./x-pack/plugins/canvas/tsconfig.json" }, { "path": "./x-pack/plugins/cases/tsconfig.json" }, { "path": "./x-pack/plugins/cloud/tsconfig.json" }, - { "path": "./x-pack/plugins/console_extensions/tsconfig.json" }, { "path": "./x-pack/plugins/dashboard_enhanced/tsconfig.json" }, { "path": "./x-pack/plugins/data_enhanced/tsconfig.json" }, { "path": "./x-pack/plugins/dashboard_mode/tsconfig.json" }, diff --git a/x-pack/plugins/apm/common/agent_configuration/constants.ts b/x-pack/plugins/apm/common/agent_configuration/constants.ts new file mode 100644 index 0000000000000..6ca70606cece0 --- /dev/null +++ b/x-pack/plugins/apm/common/agent_configuration/constants.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import * as t from 'io-ts'; + +export enum AgentConfigurationPageStep { + ChooseService = 'choose-service-step', + ChooseSettings = 'choose-settings-step', + Review = 'review-step', +} + +export const agentConfigurationPageStepRt = t.union([ + t.literal(AgentConfigurationPageStep.ChooseService), + t.literal(AgentConfigurationPageStep.ChooseSettings), + t.literal(AgentConfigurationPageStep.Review), +]); diff --git a/x-pack/plugins/apm/server/utils/queries.test.ts b/x-pack/plugins/apm/common/utils/environment_query.test.ts similarity index 80% rename from x-pack/plugins/apm/server/utils/queries.test.ts rename to x-pack/plugins/apm/common/utils/environment_query.test.ts index 9fa97940db8f3..a4ffec0d64d3e 100644 --- a/x-pack/plugins/apm/server/utils/queries.test.ts +++ b/x-pack/plugins/apm/common/utils/environment_query.test.ts @@ -5,9 +5,9 @@ * 2.0. */ -import { SERVICE_ENVIRONMENT } from '../../common/elasticsearch_fieldnames'; -import { ENVIRONMENT_NOT_DEFINED } from '../../common/environment_filter_values'; -import { environmentQuery } from './queries'; +import { SERVICE_ENVIRONMENT } from '../elasticsearch_fieldnames'; +import { ENVIRONMENT_NOT_DEFINED } from '../environment_filter_values'; +import { environmentQuery } from './environment_query'; describe('environmentQuery', () => { describe('when environment is undefined', () => { diff --git a/x-pack/plugins/apm/server/utils/queries.ts b/x-pack/plugins/apm/common/utils/environment_query.ts similarity index 68% rename from x-pack/plugins/apm/server/utils/queries.ts rename to x-pack/plugins/apm/common/utils/environment_query.ts index a82b49a84dc6e..acc75c13a0e35 100644 --- a/x-pack/plugins/apm/server/utils/queries.ts +++ b/x-pack/plugins/apm/common/utils/environment_query.ts @@ -5,15 +5,12 @@ * 2.0. */ -import { ESFilter } from '../../../../../src/core/types/elasticsearch'; -import { SERVICE_ENVIRONMENT } from '../../common/elasticsearch_fieldnames'; +import { QueryDslQueryContainer } from '@elastic/elasticsearch/api/types'; +import { SERVICE_ENVIRONMENT } from '../elasticsearch_fieldnames'; import { ENVIRONMENT_ALL, ENVIRONMENT_NOT_DEFINED, -} from '../../common/environment_filter_values'; -export { kqlQuery, rangeQuery } from '../../../observability/server'; - -type QueryDslQueryContainer = ESFilter; +} from '../environment_filter_values'; export function environmentQuery( environment?: string diff --git a/x-pack/plugins/apm/dev_docs/routing_and_linking.md b/x-pack/plugins/apm/dev_docs/routing_and_linking.md index d27513d44935f..7c5a00f43fe4b 100644 --- a/x-pack/plugins/apm/dev_docs/routing_and_linking.md +++ b/x-pack/plugins/apm/dev_docs/routing_and_linking.md @@ -12,18 +12,47 @@ The path and query string parameters are defined in the calls to `createRoute` w ### Client-side -The client-side routing uses [React Router](https://reactrouter.com/), The [`ApmRoute` component from the Elastic RUM Agent](https://www.elastic.co/guide/en/apm/agent/rum-js/current/react-integration.html), and the `history` object provided by the Kibana Platform. +The client-side routing uses `@kbn/typed-react-router-config`, which is a wrapper around [React Router](https://reactrouter.com/) and [React Router Config](https://www.npmjs.com/package/react-router-config). Its goal is to provide a layer of high-fidelity types that allows us to parse and format URLs for routes while making sure the needed parameters are provided and/or available (typed and validated at runtime). The `history` object used by React Router is injected by the Kibana Platform. -Routes are defined in [public/components/app/Main/route_config/index.tsx](../public/components/app/Main/route_config/index.tsx). These contain route definitions as well as the breadcrumb text. +Routes (and their parameters) are defined in [public/components/routing/apm_config.tsx](../public/components/routing/apm_config.tsx). #### Parameter handling -Path parameters (like `serviceName` in '/services/:serviceName/transactions') are handled by the `match.params` props passed into -routes by React Router. The types of these parameters are defined in the route definitions. - -If the parameters are not available as props you can use React Router's `useParams`, but their type definitions should be delcared inline and it's a good idea to make the properties optional if you don't know where a component will be used, since those parameters might not be available at that route. - -Query string parameters can be used in any component with `useUrlParams`. All of the available parameters are defined by this hook and its context. +Path (like `serviceName` in '/services/:serviceName/transactions') and query parameters are defined in the route definitions. + +For each parameter, an io-ts runtime type needs to be present: + +```tsx +{ + route: '/services/:serviceName', + element: , + params: t.intersection([ + t.type({ + path: t.type({ + serviceName: t.string, + }) + }), + t.partial({ + query: t.partial({ + transactionType: t.string + }) + }) + ]) +} +``` + +To be able to use the parameters, you can use `useApmParams`, which will automatically infer the parameter types from the route path: + +```ts +const { + path: { serviceName }, // string + query: { transactionType } // string | undefined +} = useApmParams('/services/:serviceName'); +``` + +`useApmParams` will strip query parameters for which there is no validation. The route path should match exactly, but you can also use wildcards: `useApmParams('/*)`. In that case, the return type will be a union type of all possible matching routes. + +Previously we used `useUrlParams` for path and query parameters, which we are trying to get away from. When possible, any usage of `useUrlParams` should be replaced by `useApmParams` or other custom hooks that use `useApmParams` internally. ## Linking @@ -31,7 +60,16 @@ Raw URLs should almost never be used in the APM UI. Instead, we have mechanisms ### In-app linking -Links that stay inside APM should use the [`getAPMHref` function and `APMLink` component](../public/components/shared/Links/apm/APMLink.tsx). Other components inside that directory contain other functions and components that provide the same functionality for linking to more specific sections inside the APM plugin. +For links that stay inside APM, the preferred way of linking is to call the `useApmRouter` hook, and call `router.link` with the route path and required path and query parameters: + +```ts +const apmRouter = useApmRouter(); +const serviceOverviewLink = apmRouter.link('/services/:serviceName', { path: { serviceName: 'opbeans-java' }, query: { transactionType: 'request' }}); +``` + + If you're not in React context, you can also import `apmRouter` directly and call its `link` function - but you have to prepend the basePath manually in that case. + +We also have the [`getAPMHref` function and `APMLink` component](../public/components/shared/Links/apm/APMLink.tsx), but we should consider them deprecated, in favor of `router.link`. Other components inside that directory contain other functions and components that provide the same functionality for linking to more specific sections inside the APM plugin. ### Cross-app linking diff --git a/x-pack/plugins/apm/public/components/alerting/alerting_flyout/index.tsx b/x-pack/plugins/apm/public/components/alerting/alerting_flyout/index.tsx index eef3271d5932d..70da0be7ebae5 100644 --- a/x-pack/plugins/apm/public/components/alerting/alerting_flyout/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/alerting_flyout/index.tsx @@ -14,7 +14,7 @@ import { import { getInitialAlertValues } from '../get_initial_alert_values'; import { ApmPluginStartDeps } from '../../../plugin'; import { useServiceName } from '../../../hooks/use_service_name'; -import { ApmServiceContextProvider } from '../../../context/apm_service/apm_service_context'; + interface Props { addFlyoutVisible: boolean; setAddFlyoutVisibility: React.Dispatch>; @@ -44,11 +44,5 @@ export function AlertingFlyout(props: Props) { /* eslint-disable-next-line react-hooks/exhaustive-deps */ [alertType, onCloseAddFlyout, services.triggersActionsUi] ); - return ( - <> - {addFlyoutVisible && ( - {addAlertFlyout} - )} - - ); + return <>{addFlyoutVisible && addAlertFlyout}; } diff --git a/x-pack/plugins/apm/public/components/alerting/error_count_alert_trigger/index.tsx b/x-pack/plugins/apm/public/components/alerting/error_count_alert_trigger/index.tsx index 811353067ab60..4ed3b9eeb19ff 100644 --- a/x-pack/plugins/apm/public/components/alerting/error_count_alert_trigger/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/error_count_alert_trigger/index.tsx @@ -18,7 +18,7 @@ import { ChartPreview } from '../chart_preview'; import { EnvironmentField, IsAboveField, ServiceField } from '../fields'; import { getAbsoluteTimeRange } from '../helper'; import { ServiceAlertTrigger } from '../service_alert_trigger'; -import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; +import { useServiceName } from '../../../hooks/use_service_name'; export interface AlertParams { windowSize: number; @@ -37,12 +37,12 @@ interface Props { export function ErrorCountAlertTrigger(props: Props) { const { setAlertParams, setAlertProperty, alertParams } = props; - const { serviceName: serviceNameFromContext } = useApmServiceContext(); + const serviceNameFromUrl = useServiceName(); const { urlParams } = useUrlParams(); const { start, end, environment: environmentFromUrl } = urlParams; const { environmentOptions } = useEnvironmentsFetcher({ - serviceName: serviceNameFromContext, + serviceName: serviceNameFromUrl, start, end, }); @@ -56,7 +56,7 @@ export function ErrorCountAlertTrigger(props: Props) { windowSize: 1, windowUnit: 'm', environment: environmentFromUrl || ENVIRONMENT_ALL.value, - serviceName: serviceNameFromContext, + serviceName: serviceNameFromUrl, } ); diff --git a/x-pack/plugins/apm/public/components/alerting/transaction_duration_alert_trigger/index.tsx b/x-pack/plugins/apm/public/components/alerting/transaction_duration_alert_trigger/index.tsx index 8f2713685127e..fa5f394d1747e 100644 --- a/x-pack/plugins/apm/public/components/alerting/transaction_duration_alert_trigger/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/transaction_duration_alert_trigger/index.tsx @@ -12,10 +12,13 @@ import React from 'react'; import { ForLastExpression } from '../../../../../triggers_actions_ui/public'; import { ENVIRONMENT_ALL } from '../../../../common/environment_filter_values'; import { getDurationFormatter } from '../../../../common/utils/formatters'; -import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; +import { getTransactionType } from '../../../context/apm_service/apm_service_context'; +import { useServiceAgentNameFetcher } from '../../../context/apm_service/use_service_agent_name_fetcher'; +import { useServiceTransactionTypesFetcher } from '../../../context/apm_service/use_service_transaction_types_fetcher'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; import { useEnvironmentsFetcher } from '../../../hooks/use_environments_fetcher'; import { useFetcher } from '../../../hooks/use_fetcher'; +import { useServiceName } from '../../../hooks/use_service_name'; import { getMaxY, getResponseTimeTickFormatter, @@ -74,11 +77,18 @@ export function TransactionDurationAlertTrigger(props: Props) { const { start, end, environment: environmentFromUrl } = urlParams; - const { + const serviceNameFromUrl = useServiceName(); + + const transactionTypes = useServiceTransactionTypesFetcher( + serviceNameFromUrl + ); + const { agentName } = useServiceAgentNameFetcher(serviceNameFromUrl); + + const transactionTypeFromUrl = getTransactionType({ + transactionType: urlParams.transactionType, transactionTypes, - transactionType: transactionTypeFromContext, - serviceName: serviceNameFromContext, - } = useApmServiceContext(); + agentName, + }); const params = defaults( { @@ -90,8 +100,8 @@ export function TransactionDurationAlertTrigger(props: Props) { threshold: 1500, windowSize: 5, windowUnit: 'm', - transactionType: transactionTypeFromContext, - serviceName: serviceNameFromContext, + transactionType: transactionTypeFromUrl, + serviceName: serviceNameFromUrl, } ); diff --git a/x-pack/plugins/apm/public/components/alerting/transaction_duration_anomaly_alert_trigger/index.tsx b/x-pack/plugins/apm/public/components/alerting/transaction_duration_anomaly_alert_trigger/index.tsx index 11307b1cd5ae3..bdf20bc14ad90 100644 --- a/x-pack/plugins/apm/public/components/alerting/transaction_duration_anomaly_alert_trigger/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/transaction_duration_anomaly_alert_trigger/index.tsx @@ -23,7 +23,10 @@ import { ServiceField, TransactionTypeField, } from '../fields'; -import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; +import { useServiceName } from '../../../hooks/use_service_name'; +import { useServiceTransactionTypesFetcher } from '../../../context/apm_service/use_service_transaction_types_fetcher'; +import { useServiceAgentNameFetcher } from '../../../context/apm_service/use_service_agent_name_fetcher'; +import { getTransactionType } from '../../../context/apm_service/apm_service_context'; interface AlertParams { windowSize: number; @@ -47,11 +50,19 @@ interface Props { export function TransactionDurationAnomalyAlertTrigger(props: Props) { const { setAlertParams, alertParams, setAlertProperty } = props; const { urlParams } = useUrlParams(); - const { - serviceName: serviceNameFromContext, - transactionType: transactionTypeFromContext, + + const serviceNameFromUrl = useServiceName(); + + const transactionTypes = useServiceTransactionTypesFetcher( + serviceNameFromUrl + ); + const { agentName } = useServiceAgentNameFetcher(serviceNameFromUrl); + + const transactionTypeFromUrl = getTransactionType({ + transactionType: urlParams.transactionType, transactionTypes, - } = useApmServiceContext(); + agentName, + }); const { start, end, environment: environmentFromUrl } = urlParams; @@ -62,10 +73,10 @@ export function TransactionDurationAnomalyAlertTrigger(props: Props) { { windowSize: 15, windowUnit: 'm', - transactionType: transactionTypeFromContext, + transactionType: transactionTypeFromUrl, environment: environmentFromUrl || ENVIRONMENT_ALL.value, anomalySeverityType: ANOMALY_SEVERITY.CRITICAL, - serviceName: serviceNameFromContext, + serviceName: serviceNameFromUrl, } ); diff --git a/x-pack/plugins/apm/public/components/alerting/transaction_error_rate_alert_trigger/index.tsx b/x-pack/plugins/apm/public/components/alerting/transaction_error_rate_alert_trigger/index.tsx index 4eb0b0e797571..cb519996f9496 100644 --- a/x-pack/plugins/apm/public/components/alerting/transaction_error_rate_alert_trigger/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/transaction_error_rate_alert_trigger/index.tsx @@ -10,7 +10,6 @@ import { defaults } from 'lodash'; import { ForLastExpression } from '../../../../../triggers_actions_ui/public'; import { ENVIRONMENT_ALL } from '../../../../common/environment_filter_values'; import { asPercent } from '../../../../common/utils/formatters'; -import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; import { useEnvironmentsFetcher } from '../../../hooks/use_environments_fetcher'; import { useFetcher } from '../../../hooks/use_fetcher'; @@ -23,6 +22,10 @@ import { } from '../fields'; import { getAbsoluteTimeRange } from '../helper'; import { ServiceAlertTrigger } from '../service_alert_trigger'; +import { useServiceName } from '../../../hooks/use_service_name'; +import { useServiceTransactionTypesFetcher } from '../../../context/apm_service/use_service_transaction_types_fetcher'; +import { useServiceAgentNameFetcher } from '../../../context/apm_service/use_service_agent_name_fetcher'; +import { getTransactionType } from '../../../context/apm_service/apm_service_context'; interface AlertParams { windowSize: number; @@ -42,28 +45,36 @@ interface Props { export function TransactionErrorRateAlertTrigger(props: Props) { const { setAlertParams, alertParams, setAlertProperty } = props; const { urlParams } = useUrlParams(); - const { - transactionType: transactionTypeFromContext, + + const serviceNameFromUrl = useServiceName(); + + const transactionTypes = useServiceTransactionTypesFetcher( + serviceNameFromUrl + ); + const { agentName } = useServiceAgentNameFetcher(serviceNameFromUrl); + + const transactionTypeFromUrl = getTransactionType({ + transactionType: urlParams.transactionType, transactionTypes, - serviceName: serviceNameFromContext, - } = useApmServiceContext(); + agentName, + }); const { start, end, environment: environmentFromUrl } = urlParams; - const params = defaults, AlertParams>( + const params = defaults( + { ...alertParams }, { threshold: 30, windowSize: 5, windowUnit: 'm', - transactionType: transactionTypeFromContext, + transactionType: transactionTypeFromUrl, environment: environmentFromUrl || ENVIRONMENT_ALL.value, - serviceName: serviceNameFromContext, - }, - alertParams + serviceName: serviceNameFromUrl, + } ); const { environmentOptions } = useEnvironmentsFetcher({ - serviceName: serviceNameFromContext, + serviceName: params.serviceName, start, end, }); diff --git a/x-pack/plugins/apm/public/components/app/Settings/agent_configurations/List/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/agent_configurations/List/index.tsx index 93ae8b270b5de..51a025df88d8e 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/agent_configurations/List/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/agent_configurations/List/index.tsx @@ -16,16 +16,12 @@ import { import { i18n } from '@kbn/i18n'; import { isEmpty } from 'lodash'; import React, { useState } from 'react'; -import { useLocation } from 'react-router-dom'; +import { useApmRouter } from '../../../../../hooks/use_apm_router'; import { APIReturnType } from '../../../../../services/rest/createCallApmApi'; import { getOptionLabel } from '../../../../../../common/agent_configuration/all_option'; import { useApmPluginContext } from '../../../../../context/apm_plugin/use_apm_plugin_context'; import { FETCH_STATUS } from '../../../../../hooks/use_fetcher'; import { useTheme } from '../../../../../hooks/use_theme'; -import { - createAgentConfigurationHref, - editAgentConfigurationHref, -} from '../../../../shared/Links/apm/agentConfigurationLinks'; import { LoadingStatePrompt } from '../../../../shared/LoadingStatePrompt'; import { ITableColumn, ManagedTable } from '../../../../shared/managed_table'; import { TimestampTooltip } from '../../../../shared/TimestampTooltip'; @@ -46,13 +42,17 @@ export function AgentConfigurationList({ }: Props) { const { core } = useApmPluginContext(); const canSave = core.application.capabilities.apm.save; - const { basePath } = core.http; - const { search } = useLocation(); const theme = useTheme(); const [configToBeDeleted, setConfigToBeDeleted] = useState( null ); + const apmRouter = useApmRouter(); + + const createAgentConfigurationHref = apmRouter.link( + '/settings/agent-configuration/create' + ); + const emptyStatePrompt = ( {i18n.translate( @@ -159,7 +159,12 @@ export function AgentConfigurationList({ flush="left" size="s" color="primary" - href={editAgentConfigurationHref(config.service, search, basePath)} + href={apmRouter.link('/settings/agent-configuration/edit', { + query: { + name: config.service.name, + environment: config.service.environment, + }, + })} > {getOptionLabel(config.service.name)} @@ -195,11 +200,12 @@ export function AgentConfigurationList({ ), }, diff --git a/x-pack/plugins/apm/public/components/app/Settings/agent_configurations/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/agent_configurations/index.tsx index 1ca7f46a0b26f..f7cfc56bf4eac 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/agent_configurations/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/agent_configurations/index.tsx @@ -17,11 +17,9 @@ import { import { i18n } from '@kbn/i18n'; import { isEmpty } from 'lodash'; import React from 'react'; -import { useLocation } from 'react-router-dom'; -import { useTrackPageview } from '../../../../../../observability/public'; +import { useApmRouter } from '../../../../hooks/use_apm_router'; import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; import { useFetcher } from '../../../../hooks/use_fetcher'; -import { createAgentConfigurationHref } from '../../../shared/Links/apm/agentConfigurationLinks'; import { AgentConfigurationList } from './List'; const INITIAL_DATA = { configurations: [] }; @@ -34,9 +32,6 @@ export function AgentConfigurations() { { preservePreviousData: false, showToastOnError: false } ); - useTrackPageview({ app: 'apm', path: 'agent_configuration' }); - useTrackPageview({ app: 'apm', path: 'agent_configuration', delay: 15000 }); - const hasConfigurations = !isEmpty(data.configurations); return ( @@ -76,10 +71,10 @@ export function AgentConfigurations() { } function CreateConfigurationButton() { + const href = useApmRouter().link('/settings/agent-configuration/create'); + const { core } = useApmPluginContext(); - const { basePath } = core.http; - const { search } = useLocation(); - const href = createAgentConfigurationHref(search, basePath); + const canSave = core.application.capabilities.apm.save; return ( diff --git a/x-pack/plugins/apm/public/components/app/Settings/schema/confirm_switch_modal.tsx b/x-pack/plugins/apm/public/components/app/Settings/schema/confirm_switch_modal.tsx index 04817aaf84f3e..5bb93c251777f 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/schema/confirm_switch_modal.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/schema/confirm_switch_modal.tsx @@ -15,6 +15,7 @@ import { htmlIdGenerator, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { useUiTracker } from '../../../../../../observability/public'; import { ElasticDocsLink } from '../../../shared/Links/ElasticDocsLink'; interface Props { @@ -27,6 +28,7 @@ export function ConfirmSwitchModal({ onCancel, unsupportedConfigs, }: Props) { + const trackApmEvent = useUiTracker({ app: 'apm' }); const [isConfirmChecked, setIsConfirmChecked] = useState(false); const hasUnsupportedConfigs = !!unsupportedConfigs.length; return ( @@ -48,7 +50,12 @@ export function ConfirmSwitchModal({ } )} defaultFocusedButton="confirm" - onConfirm={onConfirm} + onConfirm={() => { + trackApmEvent({ + metric: 'confirm_data_stream_switch', + }); + onConfirm(); + }} confirmButtonDisabled={!isConfirmChecked} >

diff --git a/x-pack/plugins/apm/public/components/app/Settings/schema/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/schema/index.tsx index 5a67ce28e9e1a..0c95648a1cefc 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/schema/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/schema/index.tsx @@ -142,9 +142,7 @@ async function createCloudApmPackagePolicy( ) { updateLocalStorage(FETCH_STATUS.LOADING); try { - const { - cloud_apm_package_policy: cloudApmPackagePolicy, - } = await callApmApi({ + const { cloudApmPackagePolicy } = await callApmApi({ endpoint: 'POST /api/apm/fleet/cloud_apm_package_policy', signal: null, }); diff --git a/x-pack/plugins/apm/public/components/app/TraceLink/index.tsx b/x-pack/plugins/apm/public/components/app/TraceLink/index.tsx index d0c2b5c598039..36ebb239fd7dd 100644 --- a/x-pack/plugins/apm/public/components/app/TraceLink/index.tsx +++ b/x-pack/plugins/apm/public/components/app/TraceLink/index.tsx @@ -7,22 +7,23 @@ import { EuiEmptyPrompt } from '@elastic/eui'; import React from 'react'; -import { Redirect, RouteComponentProps } from 'react-router-dom'; +import { Redirect } from 'react-router-dom'; import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; -import { useUrlParams } from '../../../context/url_params_context/use_url_params'; import { getRedirectToTransactionDetailPageUrl } from './get_redirect_to_transaction_detail_page_url'; import { getRedirectToTracePageUrl } from './get_redirect_to_trace_page_url'; +import { useApmParams } from '../../../hooks/use_apm_params'; const CentralizedContainer = euiStyled.div` height: 100%; display: flex; `; -export function TraceLink({ match }: RouteComponentProps<{ traceId: string }>) { - const { traceId } = match.params; - const { urlParams } = useUrlParams(); - const { rangeFrom, rangeTo } = urlParams; +export function TraceLink() { + const { + path: { traceId }, + query: { rangeFrom, rangeTo }, + } = useApmParams('/link-to/trace/:traceId'); const { data = { transaction: null }, status } = useFetcher( (callApmApi) => { diff --git a/x-pack/plugins/apm/public/components/app/TraceLink/trace_link.test.tsx b/x-pack/plugins/apm/public/components/app/TraceLink/trace_link.test.tsx index c78e9fc0a107a..0661b5ddc871d 100644 --- a/x-pack/plugins/apm/public/components/app/TraceLink/trace_link.test.tsx +++ b/x-pack/plugins/apm/public/components/app/TraceLink/trace_link.test.tsx @@ -8,7 +8,7 @@ import { act, render, waitFor } from '@testing-library/react'; import { shallow } from 'enzyme'; import React, { ReactNode } from 'react'; -import { MemoryRouter, RouteComponentProps } from 'react-router-dom'; +import { MemoryRouter } from 'react-router-dom'; import { TraceLink } from './'; import { ApmPluginContextValue } from '../../../context/apm_plugin/apm_plugin_context'; import { @@ -17,6 +17,7 @@ import { } from '../../../context/apm_plugin/mock_apm_plugin_context'; import * as hooks from '../../../hooks/use_fetcher'; import * as urlParamsHooks from '../../../context/url_params_context/use_url_params'; +import * as useApmParamsHooks from '../../../hooks/use_apm_params'; function Wrapper({ children }: { children?: ReactNode }) { return ( @@ -46,12 +47,19 @@ describe('TraceLink', () => { }); it('renders a transition page', async () => { - const props = ({ - match: { params: { traceId: 'x' } }, - } as unknown) as RouteComponentProps<{ traceId: string }>; + jest.spyOn(useApmParamsHooks as any, 'useApmParams').mockReturnValue({ + path: { + traceId: 'x', + }, + query: { + rangeFrom: 'now-24h', + rangeTo: 'now', + }, + }); + let result; act(() => { - const component = render(, renderOptions); + const component = render(, renderOptions); result = component.getByText('Fetching trace...'); }); @@ -76,10 +84,17 @@ describe('TraceLink', () => { refetch: jest.fn(), }); - const props = ({ - match: { params: { traceId: '123' } }, - } as unknown) as RouteComponentProps<{ traceId: string }>; - const component = shallow(); + jest.spyOn(useApmParamsHooks as any, 'useApmParams').mockReturnValue({ + path: { + traceId: '123', + }, + query: { + rangeFrom: 'now-24h', + rangeTo: 'now', + }, + }); + + const component = shallow(); expect(component.prop('to')).toEqual( '/traces?kuery=trace.id%2520%253A%2520%2522123%2522&rangeFrom=now-24h&rangeTo=now' @@ -93,10 +108,7 @@ describe('TraceLink', () => { rangeId: 0, refreshTimeRange: jest.fn(), uxUiFilters: {}, - urlParams: { - rangeFrom: 'now-24h', - rangeTo: 'now', - }, + urlParams: {}, }); }); @@ -116,10 +128,17 @@ describe('TraceLink', () => { refetch: jest.fn(), }); - const props = ({ - match: { params: { traceId: '123' } }, - } as unknown) as RouteComponentProps<{ traceId: string }>; - const component = shallow(); + jest.spyOn(useApmParamsHooks as any, 'useApmParams').mockReturnValue({ + path: { + traceId: '123', + }, + query: { + rangeFrom: 'now-24h', + rangeTo: 'now', + }, + }); + + const component = shallow(); expect(component.prop('to')).toEqual( '/services/foo/transactions/view?traceId=123&transactionId=456&transactionName=bar&transactionType=GET&rangeFrom=now-24h&rangeTo=now' diff --git a/x-pack/plugins/apm/public/components/app/breadcrumb/index.tsx b/x-pack/plugins/apm/public/components/app/breadcrumb/index.tsx new file mode 100644 index 0000000000000..5cc1293d39f7d --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/breadcrumb/index.tsx @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; +import { useBreadcrumb } from '../../../context/breadcrumbs/use_breadcrumb'; + +export const Breadcrumb = ({ + title, + href, + children, +}: { + title: string; + href: string; + children: React.ReactElement; +}) => { + const { core } = useApmPluginContext(); + useBreadcrumb({ title, href: core.http.basePath.prepend('/app/apm' + href) }); + + return children; +}; diff --git a/x-pack/plugins/apm/public/components/app/correlations/error_correlations.tsx b/x-pack/plugins/apm/public/components/app/correlations/error_correlations.tsx index f0d7f8d60eb6c..7202231fa66b2 100644 --- a/x-pack/plugins/apm/public/components/app/correlations/error_correlations.tsx +++ b/x-pack/plugins/apm/public/components/app/correlations/error_correlations.tsx @@ -18,8 +18,8 @@ import { import { EuiFlexGroup, EuiFlexItem, EuiText, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useState } from 'react'; -import { useParams } from 'react-router-dom'; import { useUiTracker } from '../../../../../observability/public'; +import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; import { useLocalStorage } from '../../../hooks/useLocalStorage'; import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; @@ -51,7 +51,7 @@ export function ErrorCorrelations({ onClose }: Props) { setSelectedSignificantTerm, ] = useState(null); - const { serviceName } = useParams<{ serviceName?: string }>(); + const { serviceName } = useApmServiceContext(); const { urlParams } = useUrlParams(); const { environment, diff --git a/x-pack/plugins/apm/public/components/app/correlations/index.tsx b/x-pack/plugins/apm/public/components/app/correlations/index.tsx index 2b32ece14e5cd..31232e818cd9f 100644 --- a/x-pack/plugins/apm/public/components/app/correlations/index.tsx +++ b/x-pack/plugins/apm/public/components/app/correlations/index.tsx @@ -23,7 +23,7 @@ import { EuiBetaBadge, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { useHistory, useParams } from 'react-router-dom'; +import { useHistory } from 'react-router-dom'; import { MlLatencyCorrelations } from './ml_latency_correlations'; import { ErrorCorrelations } from './error_correlations'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; @@ -49,6 +49,7 @@ import { SERVICE_NAME, TRANSACTION_NAME, } from '../../../../common/elasticsearch_fieldnames'; +import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; const errorRateTab = { key: 'errorRate', @@ -70,7 +71,7 @@ export function Correlations() { const license = useLicenseContext(); const hasActivePlatinumLicense = isActivePlatinumLicense(license); const { urlParams } = useUrlParams(); - const { serviceName } = useParams<{ serviceName: string }>(); + const { serviceName } = useApmServiceContext(); const history = useHistory(); const [isFlyoutVisible, setIsFlyoutVisible] = useState(false); @@ -104,16 +105,7 @@ export function Correlations() { width: '20%', }); } - if (urlParams.transactionName) { - properties.push({ - label: i18n.translate('xpack.apm.correlations.transactionLabel', { - defaultMessage: 'Transaction', - }), - fieldName: TRANSACTION_NAME, - val: urlParams.transactionName, - width: '20%', - }); - } + if (urlParams.environment) { properties.push({ label: i18n.translate('xpack.apm.correlations.environmentLabel', { @@ -125,6 +117,17 @@ export function Correlations() { }); } + if (urlParams.transactionName) { + properties.push({ + label: i18n.translate('xpack.apm.correlations.transactionLabel', { + defaultMessage: 'Transaction', + }), + fieldName: TRANSACTION_NAME, + val: urlParams.transactionName, + width: '20%', + }); + } + return properties; }, [serviceName, urlParams.environment, urlParams.transactionName]); diff --git a/x-pack/plugins/apm/public/components/app/correlations/latency_correlations.tsx b/x-pack/plugins/apm/public/components/app/correlations/latency_correlations.tsx index e65bad8088c17..400a9f227959f 100644 --- a/x-pack/plugins/apm/public/components/app/correlations/latency_correlations.tsx +++ b/x-pack/plugins/apm/public/components/app/correlations/latency_correlations.tsx @@ -14,7 +14,6 @@ import { Settings, } from '@elastic/charts'; import React, { useState } from 'react'; -import { useParams } from 'react-router-dom'; import { EuiTitle, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { getDurationFormatter } from '../../../../common/utils/formatters'; @@ -31,6 +30,7 @@ import { CustomFields, PercentileOption } from './custom_fields'; import { useFieldNames } from './use_field_names'; import { useLocalStorage } from '../../../hooks/useLocalStorage'; import { useUiTracker } from '../../../../../observability/public'; +import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; type OverallLatencyApiResponse = NonNullable< APIReturnType<'GET /api/apm/correlations/latency/overall_distribution'> @@ -50,7 +50,7 @@ export function LatencyCorrelations({ onClose }: Props) { setSelectedSignificantTerm, ] = useState(null); - const { serviceName } = useParams<{ serviceName?: string }>(); + const { serviceName } = useApmServiceContext(); const { urlParams } = useUrlParams(); const { environment, diff --git a/x-pack/plugins/apm/public/components/app/correlations/ml_latency_correlations.tsx b/x-pack/plugins/apm/public/components/app/correlations/ml_latency_correlations.tsx index f9536353747ee..319f8f70414dd 100644 --- a/x-pack/plugins/apm/public/components/app/correlations/ml_latency_correlations.tsx +++ b/x-pack/plugins/apm/public/components/app/correlations/ml_latency_correlations.tsx @@ -6,7 +6,7 @@ */ import React, { useEffect, useMemo, useState } from 'react'; -import { useHistory, useParams } from 'react-router-dom'; +import { useHistory } from 'react-router-dom'; import { EuiIcon, EuiBasicTableColumn, @@ -36,6 +36,8 @@ import { useCorrelations } from './use_correlations'; import { push } from '../../shared/Links/url_helpers'; import { useUiTracker } from '../../../../../observability/public'; import { asPreciseDecimal } from '../../../../common/utils/formatters'; +import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; +import { LatencyCorrelationsHelpPopover } from './ml_latency_correlations_help_popover'; const DEFAULT_PERCENTILE_THRESHOLD = 95; const isErrorMessage = (arg: unknown): arg is Error => { @@ -59,18 +61,10 @@ export function MlLatencyCorrelations({ onClose }: Props) { core: { notifications }, } = useApmPluginContext(); - const { serviceName } = useParams<{ serviceName: string }>(); + const { serviceName, transactionType } = useApmServiceContext(); const { urlParams } = useUrlParams(); - const fetchOptions = useMemo( - () => ({ - ...{ - serviceName, - ...urlParams, - }, - }), - [serviceName, urlParams] - ); + const { environment, kuery, transactionName, start, end } = urlParams; const { error, @@ -84,7 +78,15 @@ export function MlLatencyCorrelations({ onClose }: Props) { } = useCorrelations({ index: 'apm-*', ...{ - ...fetchOptions, + ...{ + environment, + kuery, + serviceName, + transactionName, + transactionType, + start, + end, + }, percentileThreshold: DEFAULT_PERCENTILE_THRESHOLD, }, }); @@ -151,7 +153,7 @@ export function MlLatencyCorrelations({ onClose }: Props) { 'xpack.apm.correlations.latencyCorrelations.correlationsTable.correlationColumnDescription', { defaultMessage: - 'The impact of a field on the latency of the service, ranging from 0 to 1.', + 'The correlation score [0-1] of an attribute; the greater the score, the more an attribute increases latency.', } )} > @@ -263,20 +265,6 @@ export function MlLatencyCorrelations({ onClose }: Props) { return ( <> - -

- {i18n.translate( - 'xpack.apm.correlations.latencyCorrelations.description', - { - defaultMessage: - 'What is slowing down my service? Correlations will help discover a slower performance in a particular cohort of your data.', - } - )} -

- - - - {!isRunning && ( @@ -320,6 +308,9 @@ export function MlLatencyCorrelations({ onClose }: Props) {
+ + + @@ -332,8 +323,7 @@ export function MlLatencyCorrelations({ onClose }: Props) { { defaultMessage: 'Latency distribution for {name}', values: { - name: - fetchOptions.transactionName ?? fetchOptions.serviceName, + name: transactionName ?? serviceName, }, } )} diff --git a/x-pack/plugins/apm/public/components/app/correlations/ml_latency_correlations_help_popover.tsx b/x-pack/plugins/apm/public/components/app/correlations/ml_latency_correlations_help_popover.tsx new file mode 100644 index 0000000000000..1f9a41c1139cd --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/correlations/ml_latency_correlations_help_popover.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { HelpPopover, HelpPopoverButton } from '../help_popover/help_popover'; + +export function LatencyCorrelationsHelpPopover() { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + + return ( + { + setIsPopoverOpen(!isPopoverOpen); + }} + /> + } + closePopover={() => setIsPopoverOpen(false)} + isOpen={isPopoverOpen} + title={i18n.translate('xpack.apm.correlations.latencyPopoverTitle', { + defaultMessage: 'Latency correlations', + })} + > +

+ +

+

+ +

+

+ +

+

+ +

+

+ +

+
+ ); +} diff --git a/x-pack/plugins/apm/public/components/app/correlations/use_correlations.ts b/x-pack/plugins/apm/public/components/app/correlations/use_correlations.ts index 8c874571d23db..2baeb63fa4a23 100644 --- a/x-pack/plugins/apm/public/components/app/correlations/use_correlations.ts +++ b/x-pack/plugins/apm/public/components/app/correlations/use_correlations.ts @@ -36,6 +36,7 @@ interface RawResponse { took: number; values: SearchServiceValue[]; overallHistogram: HistogramItem[]; + log: string[]; } export const useCorrelations = (params: CorrelationsOptions) => { diff --git a/x-pack/plugins/apm/public/components/app/error_group_details/index.tsx b/x-pack/plugins/apm/public/components/app/error_group_details/index.tsx index 344393d42506f..3b7dea1e64060 100644 --- a/x-pack/plugins/apm/public/components/app/error_group_details/index.tsx +++ b/x-pack/plugins/apm/public/components/app/error_group_details/index.tsx @@ -17,9 +17,12 @@ import { import { i18n } from '@kbn/i18n'; import React from 'react'; import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; -import { useTrackPageview } from '../../../../../observability/public'; import { NOT_AVAILABLE_LABEL } from '../../../../common/i18n'; +import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; +import { useBreadcrumb } from '../../../context/breadcrumbs/use_breadcrumb'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; +import { useApmParams } from '../../../hooks/use_apm_params'; +import { useApmRouter } from '../../../hooks/use_apm_router'; import { useErrorGroupDistributionFetcher } from '../../../hooks/use_error_group_distribution_fetcher'; import { useFetcher } from '../../../hooks/use_fetcher'; import { DetailView } from './detail_view'; @@ -89,17 +92,27 @@ function ErrorGroupHeader({ ); } -interface ErrorGroupDetailsProps { - groupId: string; - serviceName: string; -} - -export function ErrorGroupDetails({ - serviceName, - groupId, -}: ErrorGroupDetailsProps) { +export function ErrorGroupDetails() { const { urlParams } = useUrlParams(); const { environment, kuery, start, end } = urlParams; + const { serviceName } = useApmServiceContext(); + + const apmRouter = useApmRouter(); + + const { + path: { groupId }, + } = useApmParams('/services/:serviceName/errors/:groupId'); + + useBreadcrumb({ + title: groupId, + href: apmRouter.link('/services/:serviceName/errors/:groupId', { + path: { + serviceName, + groupId, + }, + }), + }); + const { data: errorGroupData } = useFetcher( (callApmApi) => { if (start && end) { @@ -128,9 +141,6 @@ export function ErrorGroupDetails({ groupId, }); - useTrackPageview({ app: 'apm', path: 'error_group_details' }); - useTrackPageview({ app: 'apm', path: 'error_group_details', delay: 15000 }); - if (!errorGroupData || !errorDistributionData) { return ; } diff --git a/x-pack/plugins/apm/public/components/app/error_group_overview/index.tsx b/x-pack/plugins/apm/public/components/app/error_group_overview/index.tsx index 4c622758e6c8b..910f6aba3303d 100644 --- a/x-pack/plugins/apm/public/components/app/error_group_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/error_group_overview/index.tsx @@ -14,21 +14,25 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; -import { useTrackPageview } from '../../../../../observability/public'; +import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; +import { useApmParams } from '../../../hooks/use_apm_params'; import { useErrorGroupDistributionFetcher } from '../../../hooks/use_error_group_distribution_fetcher'; import { useFetcher } from '../../../hooks/use_fetcher'; import { ErrorDistribution } from '../error_group_details/Distribution'; import { ErrorGroupList } from './List'; -interface ErrorGroupOverviewProps { - serviceName: string; -} +export function ErrorGroupOverview() { + const { serviceName } = useApmServiceContext(); + + const { + query: { environment, kuery, sortField, sortDirection }, + } = useApmParams('/services/:serviceName/errors'); -export function ErrorGroupOverview({ serviceName }: ErrorGroupOverviewProps) { const { - urlParams: { environment, kuery, start, end, sortField, sortDirection }, + urlParams: { start, end }, } = useUrlParams(); + const { errorDistributionData } = useErrorGroupDistributionFetcher({ serviceName, groupId: undefined, @@ -60,12 +64,6 @@ export function ErrorGroupOverview({ serviceName }: ErrorGroupOverviewProps) { [environment, kuery, serviceName, start, end, sortField, sortDirection] ); - useTrackPageview({ - app: 'apm', - path: 'error_group_overview', - }); - useTrackPageview({ app: 'apm', path: 'error_group_overview', delay: 15000 }); - if (!errorDistributionData || !errorGroupListData) { return null; } diff --git a/x-pack/plugins/apm/public/components/app/help_popover/help_popover.tsx b/x-pack/plugins/apm/public/components/app/help_popover/help_popover.tsx new file mode 100644 index 0000000000000..def310f1d8140 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/help_popover/help_popover.tsx @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { ReactNode } from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiButtonIcon, + EuiLinkButtonProps, + EuiPopover, + EuiPopoverProps, + EuiPopoverTitle, + EuiText, +} from '@elastic/eui'; +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; + +const PopoverContent = euiStyled(EuiText)` + max-width: 480px; + max-height: 40vh; +`; + +export function HelpPopoverButton({ + onClick, +}: { + onClick: EuiLinkButtonProps['onClick']; +}) { + return ( + + ); +} + +export function HelpPopover({ + anchorPosition, + button, + children, + closePopover, + isOpen, + title, +}: { + anchorPosition?: EuiPopoverProps['anchorPosition']; + button: EuiPopoverProps['button']; + children: ReactNode; + closePopover: EuiPopoverProps['closePopover']; + isOpen: EuiPopoverProps['isOpen']; + title?: string; +}) { + return ( + + {title && {title}} + + {children} + + ); +} diff --git a/x-pack/plugins/canvas/public/components/debug/index.tsx b/x-pack/plugins/apm/public/components/app/help_popover/index.tsx similarity index 79% rename from x-pack/plugins/canvas/public/components/debug/index.tsx rename to x-pack/plugins/apm/public/components/app/help_popover/index.tsx index 8adec03e85408..b1d53722c7bb5 100644 --- a/x-pack/plugins/canvas/public/components/debug/index.tsx +++ b/x-pack/plugins/apm/public/components/app/help_popover/index.tsx @@ -5,4 +5,4 @@ * 2.0. */ -export { Debug } from './debug'; +export { HelpPopoverButton, HelpPopover } from './help_popover'; diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx index cac94885511c1..ef0a5f2df0434 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx @@ -9,7 +9,6 @@ import { EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useEffect } from 'react'; import { toMountPoint } from '../../../../../../../src/plugins/kibana_react/public'; -import { useTrackPageview } from '../../../../../observability/public'; import { useAnomalyDetectionJobsContext } from '../../../context/anomaly_detection_jobs/use_anomaly_detection_jobs_context'; import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; @@ -92,13 +91,6 @@ export function ServiceInventory() { const { core } = useApmPluginContext(); const { servicesData, servicesStatus } = useServicesFetcher(); - // The page is called "service inventory" to avoid confusion with the - // "service overview", but this is tracked in some dashboards because it's the - // initial landing page for APM, so it stays as "services_overview" (plural.) - // for backward compatibility. - useTrackPageview({ app: 'apm', path: 'services_overview' }); - useTrackPageview({ app: 'apm', path: 'services_overview', delay: 15000 }); - const { anomalyDetectionJobsData, anomalyDetectionJobsStatus, diff --git a/x-pack/plugins/apm/public/components/app/service_map/empty_banner.test.tsx b/x-pack/plugins/apm/public/components/app/service_map/empty_banner.test.tsx index 092ee2dd967ae..5d30c777cd595 100644 --- a/x-pack/plugins/apm/public/components/app/service_map/empty_banner.test.tsx +++ b/x-pack/plugins/apm/public/components/app/service_map/empty_banner.test.tsx @@ -8,6 +8,7 @@ import { act, waitFor } from '@testing-library/react'; import cytoscape from 'cytoscape'; import React, { ReactNode } from 'react'; +import { MemoryRouter } from 'react-router-dom'; import { MockApmPluginContextWrapper } from '../../../context/apm_plugin/mock_apm_plugin_context'; import { renderWithTheme } from '../../../utils/testHelpers'; import { CytoscapeContext } from './Cytoscape'; @@ -17,11 +18,13 @@ const cy = cytoscape({}); function wrapper({ children }: { children: ReactNode }) { return ( - - - {children} - - + + + + {children} + + + ); } diff --git a/x-pack/plugins/apm/public/components/app/service_map/index.tsx b/x-pack/plugins/apm/public/components/app/service_map/index.tsx index 582eafe7553af..b10b527de25dd 100644 --- a/x-pack/plugins/apm/public/components/app/service_map/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_map/index.tsx @@ -11,9 +11,8 @@ import { EuiLoadingSpinner, EuiPanel, } from '@elastic/eui'; -import React, { PropsWithChildren, ReactNode } from 'react'; +import React, { ReactNode } from 'react'; import { isActivePlatinumLicense } from '../../../../common/license_check'; -import { useTrackPageview } from '../../../../../observability/public'; import { invalidLicenseMessage, SERVICE_MAP_TIMEOUT_ERROR, @@ -32,10 +31,7 @@ import { Popover } from './Popover'; import { TimeoutPrompt } from './timeout_prompt'; import { useRefDimensions } from './useRefDimensions'; import { SearchBar } from '../../shared/search_bar'; - -interface ServiceMapProps { - serviceName?: string; -} +import { useServiceName } from '../../../hooks/use_service_name'; function PromptContainer({ children }: { children: ReactNode }) { return ( @@ -67,13 +63,13 @@ function LoadingSpinner() { ); } -export function ServiceMap({ - serviceName, -}: PropsWithChildren) { +export function ServiceMap() { const theme = useTheme(); const license = useLicenseContext(); const { urlParams } = useUrlParams(); + const serviceName = useServiceName(); + const { data = { elements: [] }, status, error } = useFetcher( (callApmApi) => { // When we don't have a license or a valid license, don't make the request. @@ -106,9 +102,6 @@ export function ServiceMap({ const PADDING_BOTTOM = 24; const heightWithPadding = height - PADDING_BOTTOM; - useTrackPageview({ app: 'apm', path: 'service_map' }); - useTrackPageview({ app: 'apm', path: 'service_map', delay: 15000 }); - if (!license) { return null; } diff --git a/x-pack/plugins/apm/public/components/app/service_node_metrics/index.test.tsx b/x-pack/plugins/apm/public/components/app/service_node_metrics/index.test.tsx index 8711366fdd185..bff224cdc791c 100644 --- a/x-pack/plugins/apm/public/components/app/service_node_metrics/index.test.tsx +++ b/x-pack/plugins/apm/public/components/app/service_node_metrics/index.test.tsx @@ -16,10 +16,7 @@ describe('ServiceNodeMetrics', () => { expect(() => shallow( - + ) ).not.toThrowError(); diff --git a/x-pack/plugins/apm/public/components/app/service_node_metrics/index.tsx b/x-pack/plugins/apm/public/components/app/service_node_metrics/index.tsx index 07afcbc9c4682..1b5754ef74e8b 100644 --- a/x-pack/plugins/apm/public/components/app/service_node_metrics/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_node_metrics/index.tsx @@ -19,10 +19,16 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; -import { SERVICE_NODE_NAME_MISSING } from '../../../../common/service_nodes'; +import { + getServiceNodeName, + SERVICE_NODE_NAME_MISSING, +} from '../../../../common/service_nodes'; import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; +import { useBreadcrumb } from '../../../context/breadcrumbs/use_breadcrumb'; import { ChartPointerEventContextProvider } from '../../../context/chart_pointer_event/chart_pointer_event_context'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; +import { useApmParams } from '../../../hooks/use_apm_params'; +import { useApmRouter } from '../../../hooks/use_apm_router'; import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; import { useServiceMetricChartsFetcher } from '../../../hooks/use_service_metric_charts_fetcher'; import { truncate, unit } from '../../../utils/style'; @@ -39,19 +45,33 @@ const Truncate = euiStyled.span` ${truncate(unit * 12)} `; -interface ServiceNodeMetricsProps { - serviceName: string; - serviceNodeName: string; -} - -export function ServiceNodeMetrics({ - serviceName, - serviceNodeName, -}: ServiceNodeMetricsProps) { +export function ServiceNodeMetrics() { const { urlParams: { kuery, start, end }, } = useUrlParams(); - const { agentName } = useApmServiceContext(); + const { agentName, serviceName } = useApmServiceContext(); + + const apmRouter = useApmRouter(); + + const { + path: { serviceNodeName }, + query, + } = useApmParams('/services/:serviceName/nodes/:serviceNodeName/metrics'); + + useBreadcrumb({ + title: getServiceNodeName(serviceNodeName), + href: apmRouter.link( + '/services/:serviceName/nodes/:serviceNodeName/metrics', + { + path: { + serviceName, + serviceNodeName, + }, + query, + } + ), + }); + const { data } = useServiceMetricChartsFetcher({ serviceNodeName }); const { data: { host, containerId } = INITIAL_DATA, status } = useFetcher( diff --git a/x-pack/plugins/apm/public/components/app/service_node_overview/index.tsx b/x-pack/plugins/apm/public/components/app/service_node_overview/index.tsx index 58541e2c5501b..6431a8ad6d01c 100644 --- a/x-pack/plugins/apm/public/components/app/service_node_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_node_overview/index.tsx @@ -17,6 +17,7 @@ import { asInteger, asPercent, } from '../../../../common/utils/formatters'; +import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; import { useFetcher } from '../../../hooks/use_fetcher'; import { truncate, unit } from '../../../utils/style'; @@ -31,15 +32,13 @@ const ServiceNodeName = euiStyled.div` ${truncate(8 * unit)} `; -interface ServiceNodeOverviewProps { - serviceName: string; -} - -function ServiceNodeOverview({ serviceName }: ServiceNodeOverviewProps) { +function ServiceNodeOverview() { const { urlParams: { kuery, start, end }, } = useUrlParams(); + const { serviceName } = useApmServiceContext(); + const { data } = useFetcher( (callApmApi) => { if (!start || !end) { diff --git a/x-pack/plugins/apm/public/components/app/service_overview/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/index.tsx index fce543b05c6c3..803e9c73e9925 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/index.tsx @@ -7,7 +7,6 @@ import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; import React from 'react'; -import { useTrackPageview } from '../../../../../observability/public'; import { isRumAgentName, isIosAgentName } from '../../../../common/agent_name'; import { AnnotationsContextProvider } from '../../../context/annotations/annotations_context'; import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; @@ -28,15 +27,8 @@ import { ServiceOverviewTransactionsTable } from './service_overview_transaction */ export const chartHeight = 288; -interface ServiceOverviewProps { - serviceName: string; -} - -export function ServiceOverview({ serviceName }: ServiceOverviewProps) { - const { agentName } = useApmServiceContext(); - - useTrackPageview({ app: 'apm', path: 'service_overview' }); - useTrackPageview({ app: 'apm', path: 'service_overview', delay: 15000 }); +export function ServiceOverview() { + const { agentName, serviceName } = useApmServiceContext(); // The default EuiFlexGroup breaks at 768, but we want to break at 992, so we // observe the window width and set the flex directions of rows accordingly @@ -65,7 +57,7 @@ export function ServiceOverview({ serviceName }: ServiceOverviewProps) { - + diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview.test.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview.test.tsx index 4d6c0be9ff818..19318553727cb 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview.test.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview.test.tsx @@ -8,13 +8,13 @@ import React, { ReactNode } from 'react'; import { MemoryRouter } from 'react-router-dom'; import { CoreStart } from 'src/core/public'; +import { isEqual } from 'lodash'; import { createKibanaReactContext } from '../../../../../../../src/plugins/kibana_react/public'; import { ApmPluginContextValue } from '../../../context/apm_plugin/apm_plugin_context'; import { mockApmPluginContextValue, MockApmPluginContextWrapper, } from '../../../context/apm_plugin/mock_apm_plugin_context'; -import { MockUrlParamsContextProvider } from '../../../context/url_params_context/mock_url_params_context_provider'; import * as useDynamicIndexPatternHooks from '../../../hooks/use_dynamic_index_pattern'; import { FETCH_STATUS } from '../../../hooks/use_fetcher'; import * as useAnnotationsHooks from '../../../context/annotations/use_annotations_context'; @@ -28,11 +28,27 @@ import { getCallApmApiSpy, getCreateCallApmApiSpy, } from '../../../services/rest/callApmApiSpy'; +import { fromQuery } from '../../shared/Links/url_helpers'; +import { MockUrlParamsContextProvider } from '../../../context/url_params_context/mock_url_params_context_provider'; +import { uiSettingsServiceMock } from '../../../../../../../src/core/public/mocks'; const KibanaReactContext = createKibanaReactContext({ usageCollection: { reportUiCounter: () => {} }, } as Partial); +const mockParams = { + rangeFrom: 'now-15m', + rangeTo: 'now', + latencyAggregationType: LatencyAggregationType.avg, +}; + +const location = { + pathname: '/services/test%20service%20name/overview', + search: fromQuery(mockParams), +}; + +const uiSettings = uiSettingsServiceMock.create().setup({} as any); + function Wrapper({ children }: { children?: ReactNode }) { const value = ({ ...mockApmPluginContextValue, @@ -46,16 +62,14 @@ function Wrapper({ children }: { children?: ReactNode }) { } as unknown) as ApmPluginContextValue; return ( - - + + - + {children} @@ -69,6 +83,7 @@ describe('ServiceOverview', () => { jest .spyOn(useApmServiceContextHooks, 'useApmServiceContext') .mockReturnValue({ + serviceName: 'test service name', agentName: 'java', transactionType: 'request', transactionTypes: ['request'], @@ -96,6 +111,35 @@ describe('ServiceOverview', () => { }, 'GET /api/apm/services/{serviceName}/dependencies': [], 'GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics': [], + 'GET /api/apm/services/{serviceName}/transactions/charts/latency': { + currentPeriod: { + overallAvgDuration: null, + latencyTimeseries: [], + }, + previousPeriod: { + overallAvgDuration: null, + latencyTimeseries: [], + }, + }, + 'GET /api/apm/services/{serviceName}/throughput': { + currentPeriod: [], + previousPeriod: [], + }, + 'GET /api/apm/services/{serviceName}/transactions/charts/error_rate': { + currentPeriod: { + transactionErrorRate: [], + noHits: true, + average: null, + }, + previousPeriod: { + transactionErrorRate: [], + noHits: true, + average: null, + }, + }, + 'GET /api/apm/services/{serviceName}/annotation/search': { + annotations: [], + }, }; /* eslint-enable @typescript-eslint/naming-convention */ @@ -118,16 +162,16 @@ describe('ServiceOverview', () => { status: FETCH_STATUS.SUCCESS, }); - const { findAllByText } = renderWithTheme( - , - { - wrapper: Wrapper, - } - ); + const { findAllByText } = renderWithTheme(, { + wrapper: Wrapper, + }); - await waitFor(() => - expect(callApmApiSpy).toHaveBeenCalledTimes(Object.keys(calls).length) - ); + await waitFor(() => { + const endpoints = callApmApiSpy.mock.calls.map( + (call) => call[0].endpoint + ); + return isEqual(endpoints.sort(), Object.keys(calls).sort()); + }); expect((await findAllByText('Latency')).length).toBeGreaterThan(0); }); diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_throughput_chart.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_throughput_chart.tsx index c8da31da1b5d8..1d6c538570f8f 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_throughput_chart.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_throughput_chart.tsx @@ -8,7 +8,6 @@ import { EuiPanel, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; -import { useParams } from 'react-router-dom'; import { asTransactionRate } from '../../../../common/utils/formatters'; import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; @@ -31,7 +30,7 @@ export function ServiceOverviewThroughputChart({ height?: number; }) { const theme = useTheme(); - const { serviceName } = useParams<{ serviceName?: string }>(); + const { urlParams: { environment, @@ -42,7 +41,8 @@ export function ServiceOverviewThroughputChart({ comparisonType, }, } = useUrlParams(); - const { transactionType } = useApmServiceContext(); + + const { transactionType, serviceName } = useApmServiceContext(); const comparisonChartTheme = getComparisonChartTheme(theme); const { comparisonStart, comparisonEnd } = getTimeRangeComparison({ start, diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_transactions_table/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_transactions_table/index.tsx index a25bb807bdc46..952bb339cf396 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_transactions_table/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_transactions_table/index.tsx @@ -25,10 +25,6 @@ import { getTimeRangeComparison } from '../../../shared/time_comparison/get_time import { ServiceOverviewTableContainer } from '../service_overview_table_container'; import { getColumns } from './get_columns'; -interface Props { - serviceName: string; -} - type ApiResponse = APIReturnType<'GET /api/apm/services/{serviceName}/transactions/groups/main_statistics'>; const INITIAL_STATE = { transactionGroups: [] as ApiResponse['transactionGroups'], @@ -45,7 +41,7 @@ const DEFAULT_SORT = { field: 'impact' as const, }; -export function ServiceOverviewTransactionsTable({ serviceName }: Props) { +export function ServiceOverviewTransactionsTable() { const [tableOptions, setTableOptions] = useState<{ pageIndex: number; sort: { @@ -60,7 +56,7 @@ export function ServiceOverviewTransactionsTable({ serviceName }: Props) { const { pageIndex, sort } = tableOptions; const { direction, field } = sort; - const { transactionType } = useApmServiceContext(); + const { transactionType, serviceName } = useApmServiceContext(); const { urlParams: { start, diff --git a/x-pack/plugins/apm/public/components/app/service_profiling/index.tsx b/x-pack/plugins/apm/public/components/app/service_profiling/index.tsx index c6e1f575298c6..82195bc5b4d17 100644 --- a/x-pack/plugins/apm/public/components/app/service_profiling/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_profiling/index.tsx @@ -10,24 +10,24 @@ import { getValueTypeConfig, ProfilingValueType, } from '../../../../common/profiling'; +import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; +import { useApmParams } from '../../../hooks/use_apm_params'; import { useFetcher } from '../../../hooks/use_fetcher'; import { APIReturnType } from '../../../services/rest/createCallApmApi'; import { ServiceProfilingFlamegraph } from './service_profiling_flamegraph'; import { ServiceProfilingTimeline } from './service_profiling_timeline'; -interface ServiceProfilingProps { - serviceName: string; - environment?: string; -} - type ApiResponse = APIReturnType<'GET /api/apm/services/{serviceName}/profiling/timeline'>; const DEFAULT_DATA: ApiResponse = { profilingTimeline: [] }; -export function ServiceProfiling({ - serviceName, - environment, -}: ServiceProfilingProps) { +export function ServiceProfiling() { + const { serviceName } = useApmServiceContext(); + + const { + query: { environment }, + } = useApmParams('/services/:serviceName/profiling'); + const { urlParams: { kuery, start, end }, } = useUrlParams(); diff --git a/x-pack/plugins/apm/public/components/app/trace_overview/index.tsx b/x-pack/plugins/apm/public/components/app/trace_overview/index.tsx index d280b36a603ba..ccb5fea72432c 100644 --- a/x-pack/plugins/apm/public/components/app/trace_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/trace_overview/index.tsx @@ -6,7 +6,6 @@ */ import React from 'react'; -import { useTrackPageview } from '../../../../../observability/public'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; import { APIReturnType } from '../../../services/rest/createCallApmApi'; @@ -43,9 +42,6 @@ export function TraceOverview() { [environment, kuery, start, end] ); - useTrackPageview({ app: 'apm', path: 'traces_overview' }); - useTrackPageview({ app: 'apm', path: 'traces_overview', delay: 15000 }); - return ( <> diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/Distribution/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/Distribution/index.tsx index f59b3ddab7c05..a2db4cc87a81b 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/Distribution/index.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/Distribution/index.tsx @@ -25,7 +25,6 @@ import { isEmpty, keyBy } from 'lodash'; import React from 'react'; import { ValuesType } from 'utility-types'; import { getDurationFormatter } from '../../../../../common/utils/formatters'; -import type { IUrlParams } from '../../../../context/url_params_context/types'; import { FETCH_STATUS } from '../../../../hooks/use_fetcher'; import { useTheme } from '../../../../hooks/use_theme'; import { APIReturnType } from '../../../../services/rest/createCallApmApi'; @@ -94,7 +93,6 @@ export const formatYLong = (t: number) => { interface Props { distribution?: TransactionDistributionAPIResponse; - urlParams: IUrlParams; fetchStatus: FETCH_STATUS; bucketIndex: number; onBucketClick: ( @@ -104,7 +102,6 @@ interface Props { export function TransactionDistribution({ distribution, - urlParams: { transactionType }, fetchStatus, bucketIndex, onBucketClick, diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/index.tsx index 1e13e224a511a..6f0639de93e43 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/index.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/index.tsx @@ -9,9 +9,11 @@ import { EuiHorizontalRule, EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui'; import { flatten, isEmpty } from 'lodash'; import React from 'react'; import { useHistory } from 'react-router-dom'; -import { useTrackPageview } from '../../../../../observability/public'; +import { useBreadcrumb } from '../../../context/breadcrumbs/use_breadcrumb'; import { ChartPointerEventContextProvider } from '../../../context/chart_pointer_event/chart_pointer_event_context'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; +import { useApmParams } from '../../../hooks/use_apm_params'; +import { useApmRouter } from '../../../hooks/use_apm_router'; import { FETCH_STATUS } from '../../../hooks/use_fetcher'; import { useTransactionDistributionFetcher } from '../../../hooks/use_transaction_distribution_fetcher'; import { TransactionCharts } from '../../shared/charts/transaction_charts'; @@ -29,20 +31,33 @@ interface Sample { export function TransactionDetails() { const { urlParams } = useUrlParams(); const history = useHistory(); - const { - distributionData, - distributionStatus, - } = useTransactionDistributionFetcher(); const { waterfall, exceedsMax, status: waterfallStatus, } = useWaterfallFetcher(); - const { transactionName } = urlParams; - useTrackPageview({ app: 'apm', path: 'transaction_details' }); - useTrackPageview({ app: 'apm', path: 'transaction_details', delay: 15000 }); + const { path, query } = useApmParams( + '/services/:serviceName/transactions/view' + ); + + const apmRouter = useApmRouter(); + + const { transactionName } = query; + + const { + distributionData, + distributionStatus, + } = useTransactionDistributionFetcher({ transactionName }); + + useBreadcrumb({ + title: transactionName, + href: apmRouter.link('/services/:serviceName/transactions/view', { + path, + query, + }), + }); const selectedSample = flatten( distributionData.buckets.map((bucket) => bucket.samples) @@ -94,7 +109,6 @@ export function TransactionDetails() { { if (!isEmpty(bucket.samples)) { diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/index.tsx index 817e747551d95..c352afbe03ff2 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/index.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/index.tsx @@ -7,7 +7,6 @@ import React from 'react'; import { keyBy } from 'lodash'; -import { useParams } from 'react-router-dom'; import { IUrlParams } from '../../../../../context/url_params_context/types'; import { IWaterfall, @@ -15,6 +14,7 @@ import { } from './Waterfall/waterfall_helpers/waterfall_helpers'; import { Waterfall } from './Waterfall'; import { WaterfallLegends } from './WaterfallLegends'; +import { useApmServiceContext } from '../../../../../context/apm_service/use_apm_service_context'; interface Props { urlParams: IUrlParams; @@ -27,7 +27,7 @@ export function WaterfallContainer({ waterfall, exceedsMax, }: Props) { - const { serviceName } = useParams<{ serviceName: string }>(); + const { serviceName } = useApmServiceContext(); if (!waterfall) { return null; diff --git a/x-pack/plugins/apm/public/components/app/transaction_link/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_link/index.tsx index c6394f09b0d3c..25cbf2d319587 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_link/index.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_link/index.tsx @@ -7,23 +7,22 @@ import { EuiEmptyPrompt } from '@elastic/eui'; import React from 'react'; -import { Redirect, RouteComponentProps } from 'react-router-dom'; +import { Redirect } from 'react-router-dom'; import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; -import { useUrlParams } from '../../../context/url_params_context/use_url_params'; import { getRedirectToTransactionDetailPageUrl } from '../TraceLink/get_redirect_to_transaction_detail_page_url'; +import { useApmParams } from '../../../hooks/use_apm_params'; const CentralizedContainer = euiStyled.div` height: 100%; display: flex; `; -export function TransactionLink({ - match, -}: RouteComponentProps<{ transactionId: string }>) { - const { transactionId } = match.params; - const { urlParams } = useUrlParams(); - const { rangeFrom, rangeTo } = urlParams; +export function TransactionLink() { + const { + path: { transactionId }, + query: { rangeFrom, rangeTo }, + } = useApmParams('/link-to/transaction/:transactionId'); const { data = { transaction: null }, status } = useFetcher( (callApmApi) => { diff --git a/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx index 041c12822357c..819292095403a 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx @@ -17,7 +17,6 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { Location } from 'history'; import React from 'react'; import { useLocation } from 'react-router-dom'; -import { useTrackPageview } from '../../../../../observability/public'; import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; import { IUrlParams } from '../../../context/url_params_context/types'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; @@ -50,20 +49,14 @@ function getRedirectLocation({ } } -interface TransactionOverviewProps { - serviceName: string; -} - -export function TransactionOverview({ serviceName }: TransactionOverviewProps) { +export function TransactionOverview() { const location = useLocation(); const { urlParams } = useUrlParams(); - const { transactionType } = useApmServiceContext(); + const { transactionType, serviceName } = useApmServiceContext(); // redirect to first transaction type useRedirect(getRedirectLocation({ location, transactionType, urlParams })); - useTrackPageview({ app: 'apm', path: 'transaction_overview' }); - useTrackPageview({ app: 'apm', path: 'transaction_overview', delay: 15000 }); const { transactionListData, transactionListStatus, diff --git a/x-pack/plugins/apm/public/components/app/transaction_overview/transaction_overview.test.tsx b/x-pack/plugins/apm/public/components/app/transaction_overview/transaction_overview.test.tsx index 9c4c2aa11a858..afed0be7cc209 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_overview/transaction_overview.test.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_overview/transaction_overview.test.tsx @@ -9,7 +9,6 @@ import { queryByLabelText } from '@testing-library/react'; import { createMemoryHistory } from 'history'; import { CoreStart } from 'kibana/public'; import React from 'react'; -import { Router } from 'react-router-dom'; import { createKibanaReactContext } from 'src/plugins/kibana_react/public'; import { MockApmPluginContextWrapper } from '../../../context/apm_plugin/mock_apm_plugin_context'; import { ApmServiceContextProvider } from '../../../context/apm_service/apm_service_context'; @@ -63,14 +62,12 @@ function setup({ return renderWithTheme( - - - - - - - - + + + + + + ); diff --git a/x-pack/plugins/apm/public/components/app/transaction_overview/use_transaction_list.ts b/x-pack/plugins/apm/public/components/app/transaction_overview/use_transaction_list.ts index 062fd5470e60c..59207a6a499a2 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_overview/use_transaction_list.ts +++ b/x-pack/plugins/apm/public/components/app/transaction_overview/use_transaction_list.ts @@ -5,10 +5,10 @@ * 2.0. */ -import { useParams } from 'react-router-dom'; import { APIReturnType } from '../../../services/rest/createCallApmApi'; import { useFetcher } from '../../../hooks/use_fetcher'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; +import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; type TransactionsAPIResponse = APIReturnType<'GET /api/apm/services/{serviceName}/transactions/groups'>; @@ -22,7 +22,8 @@ export function useTransactionListFetcher() { const { urlParams: { environment, kuery, transactionType, start, end }, } = useUrlParams(); - const { serviceName } = useParams<{ serviceName?: string }>(); + const { serviceName } = useApmServiceContext(); + const { data = DEFAULT_RESPONSE, error, status } = useFetcher( (callApmApi) => { if (serviceName && start && end && transactionType) { diff --git a/x-pack/plugins/apm/public/components/routing/apm_route_config.tsx b/x-pack/plugins/apm/public/components/routing/apm_route_config.tsx index e00b7893b548e..099519416baf2 100644 --- a/x-pack/plugins/apm/public/components/routing/apm_route_config.tsx +++ b/x-pack/plugins/apm/public/components/routing/apm_route_config.tsx @@ -5,534 +5,68 @@ * 2.0. */ -import { i18n } from '@kbn/i18n'; +import { createRouter, Outlet, route } from '@kbn/typed-react-router-config'; +import * as t from 'io-ts'; import React from 'react'; -import { RouteComponentProps } from 'react-router-dom'; -import { getServiceNodeName } from '../../../common/service_nodes'; -import { APMRouteDefinition } from '../../application/routes'; -import { toQuery } from '../shared/Links/url_helpers'; -import { ErrorGroupDetails } from '../app/error_group_details'; -import { useApmPluginContext } from '../../context/apm_plugin/use_apm_plugin_context'; -import { ServiceNodeMetrics } from '../app/service_node_metrics'; -import { SettingsTemplate } from './templates/settings_template'; -import { AgentConfigurations } from '../app/Settings/agent_configurations'; -import { AnomalyDetection } from '../app/Settings/anomaly_detection'; -import { ApmIndices } from '../app/Settings/ApmIndices'; -import { CustomizeUI } from '../app/Settings/customize_ui'; -import { Schema } from '../app/Settings/schema'; +import { Breadcrumb } from '../app/breadcrumb'; import { TraceLink } from '../app/TraceLink'; import { TransactionLink } from '../app/transaction_link'; -import { TransactionDetails } from '../app/transaction_details'; -import { enableServiceOverview } from '../../../common/ui_settings_keys'; -import { redirectTo } from './redirect_to'; -import { ApmMainTemplate } from './templates/apm_main_template'; -import { ApmServiceTemplate } from './templates/apm_service_template'; -import { ServiceProfiling } from '../app/service_profiling'; -import { ErrorGroupOverview } from '../app/error_group_overview'; -import { ServiceMap } from '../app/service_map'; -import { ServiceNodeOverview } from '../app/service_node_overview'; -import { ServiceMetrics } from '../app/service_metrics'; -import { ServiceOverview } from '../app/service_overview'; -import { TransactionOverview } from '../app/transaction_overview'; -import { ServiceInventory } from '../app/service_inventory'; -import { TraceOverview } from '../app/trace_overview'; -import { useFetcher } from '../../hooks/use_fetcher'; -import { AgentConfigurationCreateEdit } from '../app/Settings/agent_configurations/AgentConfigurationCreateEdit'; - -// These component function definitions are used below with the `component` -// property of the route definitions. -// -// If you provide an inline function to the component prop, you would create a -// new component every render. This results in the existing component unmounting -// and the new component mounting instead of just updating the existing component. - -const ServiceInventoryTitle = i18n.translate( - 'xpack.apm.views.serviceInventory.title', - { defaultMessage: 'Services' } -); - -function ServiceInventoryView() { - return ( - - - - ); -} - -const TraceOverviewTitle = i18n.translate( - 'xpack.apm.views.traceOverview.title', - { - defaultMessage: 'Traces', - } -); - -function TraceOverviewView() { - return ( - - - - ); -} - -const ServiceMapTitle = i18n.translate('xpack.apm.views.serviceMap.title', { - defaultMessage: 'Service Map', -}); - -function ServiceMapView() { - return ( - - - - ); -} - -function ServiceDetailsErrorsRouteView( - props: RouteComponentProps<{ serviceName: string }> -) { - const { serviceName } = props.match.params; - return ( - - - - ); -} - -function ErrorGroupDetailsRouteView( - props: RouteComponentProps<{ serviceName: string; groupId: string }> -) { - const { serviceName, groupId } = props.match.params; - return ( - - - - ); -} - -function ServiceDetailsMetricsRouteView( - props: RouteComponentProps<{ serviceName: string }> -) { - const { serviceName } = props.match.params; - return ( - - - - ); -} - -function ServiceDetailsNodesRouteView( - props: RouteComponentProps<{ serviceName: string }> -) { - const { serviceName } = props.match.params; - return ( - - - - ); -} - -function ServiceDetailsOverviewRouteView( - props: RouteComponentProps<{ serviceName: string }> -) { - const { serviceName } = props.match.params; - return ( - - - - ); -} - -function ServiceDetailsServiceMapRouteView( - props: RouteComponentProps<{ serviceName: string }> -) { - const { serviceName } = props.match.params; - return ( - - - - ); -} - -function ServiceDetailsTransactionsRouteView( - props: RouteComponentProps<{ serviceName: string }> -) { - const { serviceName } = props.match.params; - return ( - - - - ); -} - -function ServiceDetailsProfilingRouteView( - props: RouteComponentProps<{ serviceName: string }> -) { - const { serviceName } = props.match.params; - return ( - - - - ); -} - -function ServiceNodeMetricsRouteView( - props: RouteComponentProps<{ - serviceName: string; - serviceNodeName: string; - }> -) { - const { serviceName, serviceNodeName } = props.match.params; - return ( - - - - ); -} - -function TransactionDetailsRouteView( - props: RouteComponentProps<{ serviceName: string }> -) { - const { serviceName } = props.match.params; - return ( - - - - ); -} - -function SettingsAgentConfigurationRouteView() { - return ( - - - - ); -} - -function SettingsAnomalyDetectionRouteView() { - return ( - - - - ); -} - -function SettingsApmIndicesRouteView() { - return ( - - - - ); -} - -function SettingsCustomizeUI() { - return ( - - - - ); -} - -function SettingsSchema() { - return ( - - - - ); -} - -export function EditAgentConfigurationRouteView(props: RouteComponentProps) { - const { search } = props.history.location; - - // typescript complains because `pageStop` does not exist in `APMQueryParams` - // Going forward we should move away from globally declared query params and this is a first step - // @ts-expect-error - const { name, environment, pageStep } = toQuery(search); - - const res = useFetcher( - (callApmApi) => { - return callApmApi({ - endpoint: 'GET /api/apm/settings/agent-configuration/view', - params: { query: { name, environment } }, - }); - }, - [name, environment] - ); - - return ( - - - - ); -} - -export function CreateAgentConfigurationRouteView(props: RouteComponentProps) { - const { search } = props.history.location; - - // Ignoring here because we specifically DO NOT want to add the query params to the global route handler - // @ts-expect-error - const { pageStep } = toQuery(search); - - return ( - - - - ); -} - -const SettingsApmIndicesTitle = i18n.translate( - 'xpack.apm.views.settings.indices.title', - { defaultMessage: 'Indices' } -); - -const SettingsAgentConfigurationTitle = i18n.translate( - 'xpack.apm.views.settings.agentConfiguration.title', - { defaultMessage: 'Agent Configuration' } -); -const CreateAgentConfigurationTitle = i18n.translate( - 'xpack.apm.views.settings.createAgentConfiguration.title', - { defaultMessage: 'Create Agent Configuration' } -); -const EditAgentConfigurationTitle = i18n.translate( - 'xpack.apm.views.settings.editAgentConfiguration.title', - { defaultMessage: 'Edit Agent Configuration' } -); -const SettingsCustomizeUITitle = i18n.translate( - 'xpack.apm.views.settings.customizeUI.title', - { defaultMessage: 'Customize app' } -); -const SettingsSchemaTitle = i18n.translate( - 'xpack.apm.views.settings.schema.title', - { defaultMessage: 'Schema' } -); -const SettingsAnomalyDetectionTitle = i18n.translate( - 'xpack.apm.views.settings.anomalyDetection.title', - { defaultMessage: 'Anomaly detection' } -); -const SettingsTitle = i18n.translate('xpack.apm.views.listSettings.title', { - defaultMessage: 'Settings', -}); +import { home } from './home'; +import { serviceDetail } from './service_detail'; +import { settings } from './settings'; /** * The array of route definitions to be used when the application * creates the routes. */ -export const apmRouteConfig: APMRouteDefinition[] = [ - /* - * Home routes - */ +const apmRoutes = route([ { - exact: true, path: '/', - render: redirectTo('/services'), - breadcrumb: 'APM', - }, - { - exact: true, - path: '/services', // !! Need to be kept in sync with the deepLinks in x-pack/plugins/apm/public/plugin.ts - component: ServiceInventoryView, - breadcrumb: ServiceInventoryTitle, - }, - { - exact: true, - path: '/traces', // !! Need to be kept in sync with the deepLinks in x-pack/plugins/apm/public/plugin.ts - component: TraceOverviewView, - breadcrumb: TraceOverviewTitle, - }, - { - exact: true, - path: '/service-map', // !! Need to be kept in sync with the deepLinks in x-pack/plugins/apm/public/plugin.ts - component: ServiceMapView, - breadcrumb: ServiceMapTitle, - }, - - /* - * Settings routes - */ - { - exact: true, - path: '/settings', - render: redirectTo('/settings/agent-configuration'), - breadcrumb: SettingsTitle, + element: ( + + + + ), + children: [settings, serviceDetail, home], }, { - exact: true, - path: '/settings/agent-configuration', - component: SettingsAgentConfigurationRouteView, - breadcrumb: SettingsAgentConfigurationTitle, - }, - { - exact: true, - path: '/settings/agent-configuration/create', - component: CreateAgentConfigurationRouteView, - breadcrumb: CreateAgentConfigurationTitle, - }, - { - exact: true, - path: '/settings/agent-configuration/edit', - breadcrumb: EditAgentConfigurationTitle, - component: EditAgentConfigurationRouteView, - }, - { - exact: true, - path: '/settings/apm-indices', - component: SettingsApmIndicesRouteView, - breadcrumb: SettingsApmIndicesTitle, - }, - { - exact: true, - path: '/settings/customize-ui', - component: SettingsCustomizeUI, - breadcrumb: SettingsCustomizeUITitle, - }, - { - exact: true, - path: '/settings/schema', - component: SettingsSchema, - breadcrumb: SettingsSchemaTitle, - }, - { - exact: true, - path: '/settings/anomaly-detection', - component: SettingsAnomalyDetectionRouteView, - breadcrumb: SettingsAnomalyDetectionTitle, - }, - - /* - * Services routes (with APM Service context) - */ - { - exact: true, - path: '/services/:serviceName', - breadcrumb: ({ match }) => match.params.serviceName, - component: RedirectToDefaultServiceRouteView, - }, - { - exact: true, - path: '/services/:serviceName/overview', - breadcrumb: i18n.translate('xpack.apm.views.overview.title', { - defaultMessage: 'Overview', - }), - component: ServiceDetailsOverviewRouteView, - }, - { - exact: true, - path: '/services/:serviceName/transactions', - component: ServiceDetailsTransactionsRouteView, - breadcrumb: i18n.translate('xpack.apm.views.transactions.title', { - defaultMessage: 'Transactions', - }), - }, - { - exact: true, - path: '/services/:serviceName/errors/:groupId', - component: ErrorGroupDetailsRouteView, - breadcrumb: ({ match }) => match.params.groupId, - }, - { - exact: true, - path: '/services/:serviceName/errors', - component: ServiceDetailsErrorsRouteView, - breadcrumb: i18n.translate('xpack.apm.views.errors.title', { - defaultMessage: 'Errors', - }), - }, - { - exact: true, - path: '/services/:serviceName/metrics', - component: ServiceDetailsMetricsRouteView, - breadcrumb: i18n.translate('xpack.apm.views.metrics.title', { - defaultMessage: 'Metrics', - }), - }, - // service nodes, only enabled for java agents for now - { - exact: true, - path: '/services/:serviceName/nodes', - component: ServiceDetailsNodesRouteView, - breadcrumb: i18n.translate('xpack.apm.views.nodes.title', { - defaultMessage: 'JVMs', - }), - }, - // node metrics - { - exact: true, - path: '/services/:serviceName/nodes/:serviceNodeName/metrics', - component: ServiceNodeMetricsRouteView, - breadcrumb: ({ match }) => getServiceNodeName(match.params.serviceNodeName), - }, - { - exact: true, - path: '/services/:serviceName/transactions/view', - component: TransactionDetailsRouteView, - breadcrumb: ({ location }) => { - const query = toQuery(location.search); - return query.transactionName as string; - }, - }, - { - exact: true, - path: '/services/:serviceName/profiling', - component: ServiceDetailsProfilingRouteView, - breadcrumb: i18n.translate('xpack.apm.views.serviceProfiling.title', { - defaultMessage: 'Profiling', - }), - }, - { - exact: true, - path: '/services/:serviceName/service-map', - component: ServiceDetailsServiceMapRouteView, - breadcrumb: i18n.translate('xpack.apm.views.serviceMap.title', { - defaultMessage: 'Service Map', - }), + path: '/link-to/transaction/:transactionId', + element: , + params: t.intersection([ + t.type({ + path: t.type({ + transactionId: t.string, + }), + }), + t.partial({ + query: t.partial({ + rangeFrom: t.string, + rangeTo: t.string, + }), + }), + ]), }, - /* - * Utilility routes - */ { - exact: true, path: '/link-to/trace/:traceId', - component: TraceLink, - breadcrumb: null, - }, - { - exact: true, - path: '/link-to/transaction/:transactionId', - component: TransactionLink, - breadcrumb: null, - }, -]; - -function RedirectToDefaultServiceRouteView( - props: RouteComponentProps<{ serviceName: string }> -) { - const { uiSettings } = useApmPluginContext().core; - const { serviceName } = props.match.params; - if (uiSettings.get(enableServiceOverview)) { - return redirectTo(`/services/${serviceName}/overview`)(props); - } - return redirectTo(`/services/${serviceName}/transactions`)(props); -} + element: , + params: t.intersection([ + t.type({ + path: t.type({ + traceId: t.string, + }), + }), + t.partial({ + query: t.partial({ + rangeFrom: t.string, + rangeTo: t.string, + }), + }), + ]), + }, +] as const); + +export type ApmRoutes = typeof apmRoutes; + +export const apmRouter = createRouter(apmRoutes); + +export type ApmRouter = typeof apmRouter; diff --git a/x-pack/plugins/apm/public/components/routing/app_root.tsx b/x-pack/plugins/apm/public/components/routing/app_root.tsx index 8fc59a01eeca0..e82897083ae02 100644 --- a/x-pack/plugins/apm/public/components/routing/app_root.tsx +++ b/x-pack/plugins/apm/public/components/routing/app_root.tsx @@ -5,11 +5,11 @@ * 2.0. */ -import { ApmRoute } from '@elastic/apm-rum-react'; import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json'; import euiLightVars from '@elastic/eui/dist/eui_theme_light.json'; +import { RouteRenderer, RouterProvider } from '@kbn/typed-react-router-config'; import React from 'react'; -import { Route, Router, Switch } from 'react-router-dom'; +import { Route } from 'react-router-dom'; import { DefaultTheme, ThemeProvider } from 'styled-components'; import { APP_WRAPPER_CLASS } from '../../../../../../src/core/public'; import { @@ -17,20 +17,21 @@ import { RedirectAppLinks, useUiSetting$, } from '../../../../../../src/plugins/kibana_react/public'; +import { HeaderMenuPortal } from '../../../../observability/public'; import { ScrollToTopOnPathChange } from '../../components/app/Main/ScrollToTopOnPathChange'; +import { AnomalyDetectionJobsContextProvider } from '../../context/anomaly_detection_jobs/anomaly_detection_jobs_context'; import { ApmPluginContext, ApmPluginContextValue, } from '../../context/apm_plugin/apm_plugin_context'; +import { useApmPluginContext } from '../../context/apm_plugin/use_apm_plugin_context'; +import { BreadcrumbsContextProvider } from '../../context/breadcrumbs/context'; import { LicenseProvider } from '../../context/license/license_context'; import { UrlParamsProvider } from '../../context/url_params_context/url_params_context'; -import { useApmBreadcrumbs } from '../../hooks/use_apm_breadcrumbs'; import { ApmPluginStartDeps } from '../../plugin'; -import { HeaderMenuPortal } from '../../../../observability/public'; import { ApmHeaderActionMenu } from '../shared/apm_header_action_menu'; -import { useApmPluginContext } from '../../context/apm_plugin/use_apm_plugin_context'; -import { AnomalyDetectionJobsContextProvider } from '../../context/anomaly_detection_jobs/anomaly_detection_jobs_context'; -import { apmRouteConfig } from './apm_route_config'; +import { apmRouter } from './apm_route_config'; +import { TrackPageview } from './track_pageview'; export function ApmAppRoot({ apmPluginContextValue, @@ -53,24 +54,24 @@ export function ApmAppRoot({ - - - - - - + + + + + + + + - - - {apmRouteConfig.map((route, i) => ( - - ))} - - - - - - + + + + + + + + + @@ -79,7 +80,6 @@ export function ApmAppRoot({ } function MountApmHeaderActionMenu() { - useApmBreadcrumbs(apmRouteConfig); const { setHeaderActionMenu } = useApmPluginContext().appMountParameters; return ( diff --git a/x-pack/plugins/apm/public/components/routing/home/index.tsx b/x-pack/plugins/apm/public/components/routing/home/index.tsx new file mode 100644 index 0000000000000..454dcdedace90 --- /dev/null +++ b/x-pack/plugins/apm/public/components/routing/home/index.tsx @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { i18n } from '@kbn/i18n'; +import { Outlet } from '@kbn/typed-react-router-config'; +import * as t from 'io-ts'; +import React from 'react'; +import { Redirect } from 'react-router-dom'; +import { Breadcrumb } from '../../app/breadcrumb'; +import { ServiceInventory } from '../../app/service_inventory'; +import { ServiceMap } from '../../app/service_map'; +import { TraceOverview } from '../../app/trace_overview'; +import { ApmMainTemplate } from '../templates/apm_main_template'; + +function page({ + path, + element, + title, +}: { + path: TPath; + element: React.ReactElement; + title: string; +}): { path: TPath; element: React.ReactElement } { + return { + path, + element: ( + + {element} + + ), + }; +} + +export const ServiceInventoryTitle = i18n.translate( + 'xpack.apm.views.serviceInventory.title', + { + defaultMessage: 'Services', + } +); + +export const home = { + path: '/', + element: , + params: t.partial({ + query: t.partial({ + rangeFrom: t.string, + rangeTo: t.string, + }), + }), + children: [ + page({ + path: '/services', + title: ServiceInventoryTitle, + element: , + }), + page({ + path: '/traces', + title: i18n.translate('xpack.apm.views.traceOverview.title', { + defaultMessage: 'Traces', + }), + element: , + }), + page({ + path: '/service-map', + title: i18n.translate('xpack.apm.views.serviceMap.title', { + defaultMessage: 'Service Map', + }), + element: , + }), + { + path: '/', + element: , + }, + ], +} as const; diff --git a/x-pack/plugins/apm/public/components/routing/route_config.test.tsx b/x-pack/plugins/apm/public/components/routing/route_config.test.tsx deleted file mode 100644 index b1d5c1a83b43b..0000000000000 --- a/x-pack/plugins/apm/public/components/routing/route_config.test.tsx +++ /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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { apmRouteConfig } from './apm_route_config'; - -describe('routes', () => { - describe('/', () => { - const route = apmRouteConfig.find((r) => r.path === '/'); - - describe('with no hash path', () => { - it('redirects to /services', () => { - const location = { hash: '', pathname: '/', search: '' }; - expect( - (route!.render!({ location } as any) as any).props.to.pathname - ).toEqual('/services'); - }); - }); - - describe('with a hash path', () => { - it('redirects to the hash path', () => { - const location = { - hash: - '#/services/opbeans-python/transactions/view?rangeFrom=now-24h&rangeTo=now&refreshInterval=10000&refreshPaused=false&traceId=d919c89dc7ca48d84b9dde1fef01d1f8&transactionId=1b542853d787ba7b&transactionName=GET%20opbeans.views.product_customers&transactionType=request&flyoutDetailTab=&waterfallItemId=1b542853d787ba7b', - pathname: '', - search: '', - }; - - expect((route!.render!({ location } as any) as any).props.to).toEqual({ - hash: '', - pathname: '/services/opbeans-python/transactions/view', - search: - '?rangeFrom=now-24h&rangeTo=now&refreshInterval=10000&refreshPaused=false&traceId=d919c89dc7ca48d84b9dde1fef01d1f8&transactionId=1b542853d787ba7b&transactionName=GET%20opbeans.views.product_customers&transactionType=request&flyoutDetailTab=&waterfallItemId=1b542853d787ba7b', - }); - }); - }); - }); -}); diff --git a/x-pack/plugins/apm/public/components/routing/service_detail/apm_service_wrapper.tsx b/x-pack/plugins/apm/public/components/routing/service_detail/apm_service_wrapper.tsx new file mode 100644 index 0000000000000..aa69aa4fa7965 --- /dev/null +++ b/x-pack/plugins/apm/public/components/routing/service_detail/apm_service_wrapper.tsx @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { Outlet } from '@kbn/typed-react-router-config'; +import { useApmParams } from '../../../hooks/use_apm_params'; +import { useApmRouter } from '../../../hooks/use_apm_router'; +import { ServiceInventoryTitle } from '../home'; +import { useBreadcrumb } from '../../../context/breadcrumbs/use_breadcrumb'; + +export function ApmServiceWrapper() { + const { + path: { serviceName }, + query, + } = useApmParams('/services/:serviceName'); + + const router = useApmRouter(); + + useBreadcrumb([ + { + title: ServiceInventoryTitle, + href: router.link('/services', { query }), + }, + { + title: serviceName, + href: router.link('/services/:serviceName', { + query, + path: { serviceName }, + }), + }, + ]); + + return ; +} diff --git a/x-pack/plugins/apm/public/components/routing/service_detail/index.tsx b/x-pack/plugins/apm/public/components/routing/service_detail/index.tsx new file mode 100644 index 0000000000000..19db296c428c8 --- /dev/null +++ b/x-pack/plugins/apm/public/components/routing/service_detail/index.tsx @@ -0,0 +1,226 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import * as t from 'io-ts'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { Outlet } from '@kbn/typed-react-router-config'; +import { ServiceOverview } from '../../app/service_overview'; +import { ApmServiceTemplate } from '../templates/apm_service_template'; +import { RedirectToDefaultServiceRouteView } from './redirect_to_default_service_route_view'; +import { TransactionOverview } from '../../app/transaction_overview'; +import { ApmServiceWrapper } from './apm_service_wrapper'; +import { ErrorGroupOverview } from '../../app/error_group_overview'; +import { ErrorGroupDetails } from '../../app/error_group_details'; +import { ServiceMetrics } from '../../app/service_metrics'; +import { ServiceNodeOverview } from '../../app/service_node_overview'; +import { ServiceNodeMetrics } from '../../app/service_node_metrics'; +import { ServiceMap } from '../../app/service_map'; +import { TransactionDetails } from '../../app/transaction_details'; +import { ServiceProfiling } from '../../app/service_profiling'; + +function page({ + path, + title, + tab, + element, + searchBarOptions, +}: { + path: TPath; + title: string; + tab: React.ComponentProps['selectedTab']; + element: React.ReactElement; + searchBarOptions?: { + showTransactionTypeSelector?: boolean; + showTimeComparison?: boolean; + hidden?: boolean; + }; +}): { + element: React.ReactElement; + path: TPath; +} { + return { + path, + element: ( + + {element} + + ), + } as any; +} + +export const serviceDetail = { + path: '/services/:serviceName', + element: , + params: t.intersection([ + t.type({ + path: t.type({ + serviceName: t.string, + }), + }), + t.partial({ + query: t.partial({ + environment: t.string, + rangeFrom: t.string, + rangeTo: t.string, + comparisonEnabled: t.string, + comparisonType: t.string, + latencyAggregationType: t.string, + transactionType: t.string, + kuery: t.string, + }), + }), + ]), + children: [ + page({ + path: '/overview', + element: , + tab: 'overview', + title: i18n.translate('xpack.apm.views.overview.title', { + defaultMessage: 'Overview', + }), + searchBarOptions: { + showTransactionTypeSelector: true, + showTimeComparison: true, + }, + }), + { + ...page({ + path: '/transactions', + tab: 'transactions', + title: i18n.translate('xpack.apm.views.transactions.title', { + defaultMessage: 'Transactions', + }), + element: , + searchBarOptions: { + showTransactionTypeSelector: true, + }, + }), + children: [ + { + path: '/view', + element: , + params: t.type({ + query: t.intersection([ + t.type({ + transactionName: t.string, + }), + t.partial({ + traceId: t.string, + transactionId: t.string, + }), + ]), + }), + }, + { + path: '/', + element: , + }, + ], + }, + { + ...page({ + path: '/errors', + tab: 'errors', + title: i18n.translate('xpack.apm.views.errors.title', { + defaultMessage: 'Errors', + }), + element: , + }), + params: t.partial({ + query: t.partial({ + sortDirection: t.string, + sortField: t.string, + pageSize: t.string, + page: t.string, + }), + }), + children: [ + { + path: '/:groupId', + element: , + params: t.type({ + path: t.type({ + groupId: t.string, + }), + }), + }, + { + path: '/', + element: , + }, + ], + }, + page({ + path: '/metrics', + tab: 'metrics', + title: i18n.translate('xpack.apm.views.metrics.title', { + defaultMessage: 'Metrics', + }), + element: , + }), + { + ...page({ + path: '/nodes', + tab: 'nodes', + title: i18n.translate('xpack.apm.views.nodes.title', { + defaultMessage: 'JVMs', + }), + element: , + }), + children: [ + { + path: '/:serviceNodeName/metrics', + element: , + params: t.type({ + path: t.type({ + serviceNodeName: t.string, + }), + }), + }, + { + path: '/', + element: , + params: t.partial({ + query: t.partial({ + sortDirection: t.string, + sortField: t.string, + pageSize: t.string, + page: t.string, + }), + }), + }, + ], + }, + page({ + path: '/service-map', + tab: 'service-map', + title: i18n.translate('xpack.apm.views.serviceMap.title', { + defaultMessage: 'Service Map', + }), + element: , + searchBarOptions: { + hidden: true, + }, + }), + page({ + path: '/profiling', + tab: 'profiling', + title: i18n.translate('xpack.apm.views.serviceProfiling.title', { + defaultMessage: 'Profiling', + }), + element: , + }), + { + path: '/', + element: , + }, + ], +} as const; diff --git a/x-pack/plugins/apm/public/components/routing/service_detail/redirect_to_default_service_route_view.tsx b/x-pack/plugins/apm/public/components/routing/service_detail/redirect_to_default_service_route_view.tsx new file mode 100644 index 0000000000000..37ec76f2b299e --- /dev/null +++ b/x-pack/plugins/apm/public/components/routing/service_detail/redirect_to_default_service_route_view.tsx @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { Redirect } from 'react-router-dom'; +import qs from 'query-string'; +import { enableServiceOverview } from '../../../../common/ui_settings_keys'; +import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; +import { useApmParams } from '../../../hooks/use_apm_params'; + +export function RedirectToDefaultServiceRouteView() { + const { + core: { uiSettings }, + } = useApmPluginContext(); + const { + path: { serviceName }, + query, + } = useApmParams('/services/:serviceName/*'); + + const search = qs.stringify(query); + + if (uiSettings.get(enableServiceOverview)) { + return ( + + ); + } + return ( + + ); +} diff --git a/x-pack/plugins/apm/public/components/routing/settings/create_agent_configuration_route_view.tsx b/x-pack/plugins/apm/public/components/routing/settings/create_agent_configuration_route_view.tsx new file mode 100644 index 0000000000000..b01882031bea5 --- /dev/null +++ b/x-pack/plugins/apm/public/components/routing/settings/create_agent_configuration_route_view.tsx @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { AgentConfigurationPageStep } from '../../../../common/agent_configuration/constants'; +import { useApmParams } from '../../../hooks/use_apm_params'; +import { AgentConfigurationCreateEdit } from '../../app/Settings/agent_configurations/AgentConfigurationCreateEdit'; + +export function CreateAgentConfigurationRouteView() { + const { + query: { pageStep = AgentConfigurationPageStep.ChooseService }, + } = useApmParams('/settings/agent-configuration/create'); + + return ; +} diff --git a/x-pack/plugins/apm/public/components/routing/settings/edit_agent_configuration_route_view.tsx b/x-pack/plugins/apm/public/components/routing/settings/edit_agent_configuration_route_view.tsx new file mode 100644 index 0000000000000..70f1ce4d1d9db --- /dev/null +++ b/x-pack/plugins/apm/public/components/routing/settings/edit_agent_configuration_route_view.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { AgentConfigurationPageStep } from '../../../../common/agent_configuration/constants'; +import { useApmParams } from '../../../hooks/use_apm_params'; +import { useFetcher } from '../../../hooks/use_fetcher'; +import { AgentConfigurationCreateEdit } from '../../app/Settings/agent_configurations/AgentConfigurationCreateEdit'; + +export function EditAgentConfigurationRouteView() { + const { + query: { + name, + environment, + pageStep = AgentConfigurationPageStep.ChooseSettings, + }, + } = useApmParams('/settings/agent-configuration/edit'); + + const res = useFetcher( + (callApmApi) => { + return callApmApi({ + endpoint: 'GET /api/apm/settings/agent-configuration/view', + params: { query: { name, environment } }, + }); + }, + [name, environment] + ); + + return ( + + ); +} diff --git a/x-pack/plugins/apm/public/components/routing/settings/index.tsx b/x-pack/plugins/apm/public/components/routing/settings/index.tsx new file mode 100644 index 0000000000000..e844f05050d17 --- /dev/null +++ b/x-pack/plugins/apm/public/components/routing/settings/index.tsx @@ -0,0 +1,140 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import * as t from 'io-ts'; +import { Outlet } from '@kbn/typed-react-router-config'; +import { i18n } from '@kbn/i18n'; +import { Redirect } from 'react-router-dom'; +import { agentConfigurationPageStepRt } from '../../../../common/agent_configuration/constants'; +import { Breadcrumb } from '../../app/breadcrumb'; +import { SettingsTemplate } from '../templates/settings_template'; +import { AgentConfigurations } from '../../app/Settings/agent_configurations'; +import { CreateAgentConfigurationRouteView } from './create_agent_configuration_route_view'; +import { EditAgentConfigurationRouteView } from './edit_agent_configuration_route_view'; +import { ApmIndices } from '../../app/Settings/ApmIndices'; +import { CustomizeUI } from '../../app/Settings/customize_ui'; +import { Schema } from '../../app/Settings/schema'; +import { AnomalyDetection } from '../../app/Settings/anomaly_detection'; + +function page({ + path, + title, + tab, + element, +}: { + path: TPath; + title: string; + tab: React.ComponentProps['selectedTab']; + element: React.ReactElement; +}): { + element: React.ReactElement; + path: TPath; +} { + return { + path, + element: ( + + {element} + + ), + } as any; +} + +export const settings = { + path: '/settings', + element: ( + + + + ), + children: [ + page({ + path: '/agent-configuration', + tab: 'agent-configurations', + title: i18n.translate( + 'xpack.apm.views.settings.agentConfiguration.title', + { defaultMessage: 'Agent Configuration' } + ), + element: , + }), + { + ...page({ + path: '/agent-configuration/create', + title: i18n.translate( + 'xpack.apm.views.settings.createAgentConfiguration.title', + { defaultMessage: 'Create Agent Configuration' } + ), + tab: 'agent-configurations', + element: , + }), + params: t.partial({ + query: t.partial({ + pageStep: agentConfigurationPageStepRt, + }), + }), + }, + { + ...page({ + path: '/agent-configuration/edit', + title: i18n.translate( + 'xpack.apm.views.settings.editAgentConfiguration.title', + { defaultMessage: 'Edit Agent Configuration' } + ), + tab: 'agent-configurations', + element: , + }), + params: t.partial({ + query: t.partial({ + name: t.string, + environment: t.string, + pageStep: agentConfigurationPageStepRt, + }), + }), + }, + page({ + path: '/apm-indices', + title: i18n.translate('xpack.apm.views.settings.indices.title', { + defaultMessage: 'Indices', + }), + tab: 'apm-indices', + element: , + }), + page({ + path: '/customize-ui', + title: i18n.translate('xpack.apm.views.settings.customizeUI.title', { + defaultMessage: 'Customize app', + }), + tab: 'customize-ui', + element: , + }), + page({ + path: '/schema', + title: i18n.translate('xpack.apm.views.settings.schema.title', { + defaultMessage: 'Schema', + }), + element: , + tab: 'schema', + }), + page({ + path: '/anomaly-detection', + title: i18n.translate('xpack.apm.views.settings.anomalyDetection.title', { + defaultMessage: 'Anomaly detection', + }), + element: , + tab: 'anomaly-detection', + }), + { + path: '/', + element: , + }, + ], +} as const; diff --git a/x-pack/plugins/apm/public/components/routing/templates/apm_service_template.tsx b/x-pack/plugins/apm/public/components/routing/templates/apm_service_template.tsx index b77b07a23455a..2e10c853f5429 100644 --- a/x-pack/plugins/apm/public/components/routing/templates/apm_service_template.tsx +++ b/x-pack/plugins/apm/public/components/routing/templates/apm_service_template.tsx @@ -16,6 +16,7 @@ import { EuiToolTip, EuiButtonEmpty, } from '@elastic/eui'; +import { omit } from 'lodash'; import { ApmMainTemplate } from './apm_main_template'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; import { ApmServiceContextProvider } from '../../../context/apm_service/apm_service_context'; @@ -28,13 +29,6 @@ import { import { ServiceIcons } from '../../shared/service_icons'; import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; -import { useErrorOverviewHref } from '../../shared/Links/apm/ErrorOverviewLink'; -import { useMetricOverviewHref } from '../../shared/Links/apm/MetricOverviewLink'; -import { useServiceMapHref } from '../../shared/Links/apm/ServiceMapLink'; -import { useServiceNodeOverviewHref } from '../../shared/Links/apm/ServiceNodeOverviewLink'; -import { useServiceOverviewHref } from '../../shared/Links/apm/service_overview_link'; -import { useServiceProfilingHref } from '../../shared/Links/apm/service_profiling_link'; -import { useTransactionsOverviewHref } from '../../shared/Links/apm/transaction_overview_link'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; import { ENVIRONMENT_NOT_DEFINED } from '../../../../common/environment_filter_values'; import { @@ -47,22 +41,25 @@ import { createExploratoryViewUrl, SeriesUrl, } from '../../../../../observability/public'; +import { useApmParams } from '../../../hooks/use_apm_params'; +import { useBreadcrumb } from '../../../context/breadcrumbs/use_breadcrumb'; +import { useApmRouter } from '../../../hooks/use_apm_router'; type Tab = NonNullable[0] & { key: + | 'overview' + | 'transactions' | 'errors' | 'metrics' | 'nodes' - | 'overview' | 'service-map' - | 'profiling' - | 'transactions'; + | 'profiling'; hidden?: boolean; }; interface Props { + title: string; children: React.ReactNode; - serviceName: string; selectedTab: Tab['key']; searchBarOptions?: React.ComponentProps; } @@ -76,12 +73,27 @@ export function ApmServiceTemplate(props: Props) { } function TemplateWithContext({ + title, children, - serviceName, selectedTab, searchBarOptions, }: Props) { - const tabs = useTabs({ serviceName, selectedTab }); + const { + path: { serviceName }, + query, + } = useApmParams('/services/:serviceName/*'); + + const router = useApmRouter(); + + const tabs = useTabs({ selectedTab }); + + useBreadcrumb({ + title, + href: router.link(`/services/:serviceName/${selectedTab}` as const, { + path: { serviceName }, + query, + }), + }); return ( diff --git a/x-pack/plugins/apm/public/components/routing/track_pageview.tsx b/x-pack/plugins/apm/public/components/routing/track_pageview.tsx new file mode 100644 index 0000000000000..20e02a505bc43 --- /dev/null +++ b/x-pack/plugins/apm/public/components/routing/track_pageview.tsx @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { useRoutePath } from '@kbn/typed-react-router-config'; +import { useTrackPageview } from '../../../../observability/public'; + +export function TrackPageview({ children }: { children: React.ReactElement }) { + const routePath = useRoutePath(); + + useTrackPageview({ app: 'apm', path: routePath }); + useTrackPageview({ app: 'apm', path: routePath, delay: 15000 }); + + return children; +} diff --git a/x-pack/plugins/apm/public/components/shared/EnvironmentFilter/index.tsx b/x-pack/plugins/apm/public/components/shared/EnvironmentFilter/index.tsx index d5a685c4ea70b..28e674bc4150b 100644 --- a/x-pack/plugins/apm/public/components/shared/EnvironmentFilter/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/EnvironmentFilter/index.tsx @@ -9,7 +9,7 @@ import { EuiSelect } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { History } from 'history'; import React from 'react'; -import { useHistory, useLocation, useParams } from 'react-router-dom'; +import { useHistory, useLocation } from 'react-router-dom'; import { ENVIRONMENT_ALL, ENVIRONMENT_NOT_DEFINED, @@ -17,6 +17,7 @@ import { import { useEnvironmentsFetcher } from '../../../hooks/use_environments_fetcher'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; import { fromQuery, toQuery } from '../Links/url_helpers'; +import { useApmParams } from '../../../hooks/use_apm_params'; function updateEnvironmentUrl( history: History, @@ -63,12 +64,12 @@ function getOptions(environments: string[]) { export function EnvironmentFilter() { const history = useHistory(); const location = useLocation(); - const { serviceName } = useParams<{ serviceName?: string }>(); + const { path } = useApmParams('/*'); const { urlParams } = useUrlParams(); const { environment, start, end } = urlParams; const { environments, status = 'loading' } = useEnvironmentsFetcher({ - serviceName, + serviceName: 'serviceName' in path ? path.serviceName : undefined, start, end, }); diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/agentConfigurationLinks.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/agentConfigurationLinks.tsx index d009f1f82b061..58c510eff13a4 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/agentConfigurationLinks.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/agentConfigurationLinks.tsx @@ -26,14 +26,3 @@ export function editAgentConfigurationHref( }, }); } - -export function createAgentConfigurationHref( - search: string, - basePath: IBasePath -) { - return getAPMHref({ - basePath, - path: '/settings/agent-configuration/create', - search, - }); -} diff --git a/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/use_transaction_breakdown.ts b/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/use_transaction_breakdown.ts index a80c859459557..233d0821a5319 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/use_transaction_breakdown.ts +++ b/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/use_transaction_breakdown.ts @@ -5,17 +5,15 @@ * 2.0. */ -import { useParams } from 'react-router-dom'; import { useFetcher } from '../../../../hooks/use_fetcher'; import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context'; export function useTransactionBreakdown() { - const { serviceName } = useParams<{ serviceName?: string }>(); const { urlParams: { environment, kuery, start, end, transactionName }, } = useUrlParams(); - const { transactionType } = useApmServiceContext(); + const { transactionType, serviceName } = useApmServiceContext(); const { data = { timeseries: undefined }, error, status } = useFetcher( (callApmApi) => { 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 a64355e47f757..e0f4ddb24c350 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 @@ -9,7 +9,6 @@ import { EuiFlexItem, EuiIconTip, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { isEmpty } from 'lodash'; import React from 'react'; -import { useParams } from 'react-router-dom'; import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context'; import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; @@ -33,9 +32,8 @@ const ShiftedEuiText = euiStyled(EuiText)` `; export function MLHeader({ hasValidMlLicense, mlJobId }: Props) { - const { serviceName } = useParams<{ serviceName?: string }>(); const { urlParams } = useUrlParams(); - const { transactionType } = useApmServiceContext(); + const { transactionType, serviceName } = useApmServiceContext(); if (!hasValidMlLicense || !mlJobId) { return null; diff --git a/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx index 96cb7c49a6710..18c765c50fbf7 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx @@ -8,7 +8,6 @@ import { EuiPanel, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; -import { useParams } from 'react-router-dom'; import { RULE_ID } from '../../../../../../rule_registry/common/technical_rule_data_field_names'; import { AlertType } from '../../../../../common/alert_types'; import { APIReturnType } from '../../../../services/rest/createCallApmApi'; @@ -52,7 +51,6 @@ export function TransactionErrorRateChart({ showAnnotations = true, }: Props) { const theme = useTheme(); - const { serviceName } = useParams<{ serviceName?: string }>(); const { urlParams: { environment, @@ -64,7 +62,7 @@ export function TransactionErrorRateChart({ comparisonType, }, } = useUrlParams(); - const { transactionType, alerts } = useApmServiceContext(); + const { serviceName, transactionType, alerts } = useApmServiceContext(); const comparisonChartThem = getComparisonChartTheme(theme); const { comparisonStart, comparisonEnd } = getTimeRangeComparison({ start, diff --git a/x-pack/plugins/apm/public/components/shared/kuery_bar/get_bool_filter.ts b/x-pack/plugins/apm/public/components/shared/kuery_bar/get_bool_filter.ts index 150a4d9efc2cb..50db1db8f38fe 100644 --- a/x-pack/plugins/apm/public/components/shared/kuery_bar/get_bool_filter.ts +++ b/x-pack/plugins/apm/public/components/shared/kuery_bar/get_bool_filter.ts @@ -14,6 +14,7 @@ import { TRANSACTION_TYPE, } from '../../../../common/elasticsearch_fieldnames'; import { UIProcessorEvent } from '../../../../common/processor_event'; +import { environmentQuery } from '../../../../common/utils/environment_query'; import { IUrlParams } from '../../../context/url_params_context/types'; export function getBoolFilter({ @@ -35,6 +36,14 @@ export function getBoolFilter({ }); } + boolFilter.push(...environmentQuery(urlParams.environment)); + + if (urlParams.transactionType) { + boolFilter.push({ + term: { [TRANSACTION_TYPE]: urlParams.transactionType }, + }); + } + switch (processorEvent) { case 'transaction': boolFilter.push({ diff --git a/x-pack/plugins/apm/public/components/shared/kuery_bar/index.tsx b/x-pack/plugins/apm/public/components/shared/kuery_bar/index.tsx index 1b503e9b05286..72c5bac1f9f17 100644 --- a/x-pack/plugins/apm/public/components/shared/kuery_bar/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/kuery_bar/index.tsx @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import { startsWith, uniqueId } from 'lodash'; import React, { useState } from 'react'; -import { useHistory, useLocation, useParams } from 'react-router-dom'; +import { useHistory, useLocation } from 'react-router-dom'; import { esKuery, IIndexPattern, @@ -16,6 +16,7 @@ import { } from '../../../../../../../src/plugins/data/public'; import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; +import { useApmParams } from '../../../hooks/use_apm_params'; import { useDynamicIndexPatternFetcher } from '../../../hooks/use_dynamic_index_pattern'; import { fromQuery, toQuery } from '../Links/url_helpers'; import { getBoolFilter } from './get_bool_filter'; @@ -34,10 +35,11 @@ function convertKueryToEsQuery(kuery: string, indexPattern: IIndexPattern) { } export function KueryBar(props: { prepend?: React.ReactNode | string }) { - const { groupId, serviceName } = useParams<{ - groupId?: string; - serviceName?: string; - }>(); + const { path } = useApmParams('/*'); + + const serviceName = 'serviceName' in path ? path.serviceName : undefined; + const groupId = 'groupId' in path ? path.groupId : undefined; + const history = useHistory(); const [state, setState] = useState({ suggestions: [], @@ -101,6 +103,7 @@ export function KueryBar(props: { prepend?: React.ReactNode | string }) { selectionStart, selectionEnd: selectionStart, useTimeRange: true, + method: 'terms_agg', })) || [] ) .filter((suggestion) => !startsWith(suggestion.text, 'span.')) diff --git a/x-pack/plugins/apm/public/context/annotations/annotations_context.tsx b/x-pack/plugins/apm/public/context/annotations/annotations_context.tsx index ea2feb3d2a4ad..78ddb1e4560c5 100644 --- a/x-pack/plugins/apm/public/context/annotations/annotations_context.tsx +++ b/x-pack/plugins/apm/public/context/annotations/annotations_context.tsx @@ -6,8 +6,8 @@ */ import React, { createContext } from 'react'; -import { useParams } from 'react-router-dom'; import { Annotation } from '../../../common/annotations'; +import { useApmParams } from '../../hooks/use_apm_params'; import { useFetcher } from '../../hooks/use_fetcher'; import { useUrlParams } from '../url_params_context/use_url_params'; @@ -22,7 +22,10 @@ export function AnnotationsContextProvider({ }: { children: React.ReactNode; }) { - const { serviceName } = useParams<{ serviceName?: string }>(); + const { path } = useApmParams('/*'); + + const serviceName = 'serviceName' in path ? path.serviceName : undefined; + const { urlParams: { environment, start, end }, } = useUrlParams(); diff --git a/x-pack/plugins/apm/public/context/apm_plugin/mock_apm_plugin_context.tsx b/x-pack/plugins/apm/public/context/apm_plugin/mock_apm_plugin_context.tsx index a356f2962b3c6..5666c64376c20 100644 --- a/x-pack/plugins/apm/public/context/apm_plugin/mock_apm_plugin_context.tsx +++ b/x-pack/plugins/apm/public/context/apm_plugin/mock_apm_plugin_context.tsx @@ -5,14 +5,18 @@ * 2.0. */ -import React, { ReactNode } from 'react'; +import React, { ReactNode, useMemo } from 'react'; import { Observable, of } from 'rxjs'; +import { RouterProvider } from '@kbn/typed-react-router-config'; +import { useHistory } from 'react-router-dom'; +import { createMemoryHistory, History } from 'history'; import { UrlService } from '../../../../../../src/plugins/share/common/url_service'; import { createObservabilityRuleTypeRegistryMock } from '../../../../observability/public'; import { ApmPluginContext, ApmPluginContextValue } from './apm_plugin_context'; import { ConfigSchema } from '../..'; import { UI_SETTINGS } from '../../../../../../src/plugins/data/common'; import { createCallApmApi } from '../../services/rest/createCallApmApi'; +import { apmRouter } from '../../components/routing/apm_route_config'; import { MlLocatorDefinition } from '../../../../ml/public'; const uiSettings: Record = { @@ -124,21 +128,32 @@ export const mockApmPluginContextValue = { export function MockApmPluginContextWrapper({ children, value = {} as ApmPluginContextValue, + history, }: { children?: React.ReactNode; value?: ApmPluginContextValue; + history?: History; }) { if (value.core) { createCallApmApi(value.core); } + + const contextHistory = useHistory(); + + const usedHistory = useMemo(() => { + return history || contextHistory || createMemoryHistory(); + }, [history, contextHistory]); + return ( - - {children} - + + + {children} + + ); } diff --git a/x-pack/plugins/apm/public/context/apm_service/apm_service_context.test.tsx b/x-pack/plugins/apm/public/context/apm_service/apm_service_context.test.tsx index 1379bd8603999..8751145081f4a 100644 --- a/x-pack/plugins/apm/public/context/apm_service/apm_service_context.test.tsx +++ b/x-pack/plugins/apm/public/context/apm_service/apm_service_context.test.tsx @@ -13,7 +13,7 @@ describe('getTransactionType', () => { expect( getTransactionType({ transactionTypes: ['worker', 'request'], - urlParams: { transactionType: 'custom' }, + transactionType: 'custom', agentName: 'nodejs', }) ).toBe('custom'); @@ -25,7 +25,6 @@ describe('getTransactionType', () => { expect( getTransactionType({ transactionTypes: [], - urlParams: {}, }) ).toBeUndefined(); }); @@ -37,7 +36,6 @@ describe('getTransactionType', () => { expect( getTransactionType({ transactionTypes: ['worker', 'request'], - urlParams: {}, agentName: 'nodejs', }) ).toEqual('request'); @@ -49,7 +47,6 @@ describe('getTransactionType', () => { expect( getTransactionType({ transactionTypes: ['worker', 'custom'], - urlParams: {}, agentName: 'nodejs', }) ).toEqual('worker'); @@ -62,7 +59,6 @@ describe('getTransactionType', () => { expect( getTransactionType({ transactionTypes: ['http-request', 'page-load'], - urlParams: {}, agentName: 'js-base', }) ).toEqual('page-load'); diff --git a/x-pack/plugins/apm/public/context/apm_service/apm_service_context.tsx b/x-pack/plugins/apm/public/context/apm_service/apm_service_context.tsx index cb826763425c2..1f454855b0f25 100644 --- a/x-pack/plugins/apm/public/context/apm_service/apm_service_context.tsx +++ b/x-pack/plugins/apm/public/context/apm_service/apm_service_context.tsx @@ -13,40 +13,39 @@ import { TRANSACTION_REQUEST, } from '../../../common/transaction_types'; import { useServiceTransactionTypesFetcher } from './use_service_transaction_types_fetcher'; -import { useUrlParams } from '../url_params_context/use_url_params'; import { useServiceAgentNameFetcher } from './use_service_agent_name_fetcher'; -import { IUrlParams } from '../url_params_context/types'; import { APIReturnType } from '../../services/rest/createCallApmApi'; import { useServiceAlertsFetcher } from './use_service_alerts_fetcher'; -import { useServiceName } from '../../hooks/use_service_name'; +import { useApmParams } from '../../hooks/use_apm_params'; export type APMServiceAlert = ValuesType< APIReturnType<'GET /api/apm/services/{serviceName}/alerts'>['alerts'] >; export const APMServiceContext = createContext<{ + serviceName: string; agentName?: string; transactionType?: string; transactionTypes: string[]; alerts: APMServiceAlert[]; - serviceName?: string; -}>({ transactionTypes: [], alerts: [] }); +}>({ serviceName: '', transactionTypes: [], alerts: [] }); export function ApmServiceContextProvider({ children, }: { children: ReactNode; }) { - const { urlParams } = useUrlParams(); - - const serviceName = useServiceName(); + const { + path: { serviceName }, + query, + } = useApmParams('/services/:serviceName'); const { agentName } = useServiceAgentNameFetcher(serviceName); const transactionTypes = useServiceTransactionTypesFetcher(serviceName); const transactionType = getTransactionType({ - urlParams, + transactionType: query.transactionType, transactionTypes, agentName, }); @@ -56,11 +55,11 @@ export function ApmServiceContextProvider({ return ( @@ -68,16 +67,16 @@ export function ApmServiceContextProvider({ } export function getTransactionType({ - urlParams, + transactionType, transactionTypes, agentName, }: { - urlParams: IUrlParams; + transactionType?: string; transactionTypes: string[]; agentName?: string; }) { - if (urlParams.transactionType) { - return urlParams.transactionType; + if (transactionType) { + return transactionType; } if (!agentName || transactionTypes.length === 0) { diff --git a/x-pack/plugins/apm/public/context/breadcrumbs/context.tsx b/x-pack/plugins/apm/public/context/breadcrumbs/context.tsx new file mode 100644 index 0000000000000..906d2b19abf9f --- /dev/null +++ b/x-pack/plugins/apm/public/context/breadcrumbs/context.tsx @@ -0,0 +1,92 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { + Route, + RouteMatch, + useMatchRoutes, +} from '@kbn/typed-react-router-config'; +import { ChromeBreadcrumb } from 'kibana/public'; +import { compact, isEqual } from 'lodash'; +import React, { createContext, useMemo, useState } from 'react'; +import { useBreadcrumbs } from '../../../../observability/public'; + +export interface Breadcrumb { + title: string; + href: string; +} + +interface BreadcrumbApi { + set(route: Route, breadcrumb: Breadcrumb[]): void; + unset(route: Route): void; + getBreadcrumbs(matches: RouteMatch[]): Breadcrumb[]; +} + +export const BreadcrumbsContext = createContext( + undefined +); + +export function BreadcrumbsContextProvider({ + children, +}: { + children: React.ReactElement; +}) { + const [, forceUpdate] = useState({}); + + const breadcrumbs = useMemo(() => { + return new Map(); + }, []); + + const matches: RouteMatch[] = useMatchRoutes(); + + const api = useMemo( + () => ({ + set(route, breadcrumb) { + if (!isEqual(breadcrumbs.get(route), breadcrumb)) { + breadcrumbs.set(route, breadcrumb); + forceUpdate({}); + } + }, + unset(route) { + if (breadcrumbs.has(route)) { + breadcrumbs.delete(route); + forceUpdate({}); + } + }, + getBreadcrumbs(currentMatches: RouteMatch[]) { + return compact( + currentMatches.flatMap((match) => { + const breadcrumb = breadcrumbs.get(match.route); + + return breadcrumb; + }) + ); + }, + }), + [breadcrumbs] + ); + + const formattedBreadcrumbs: ChromeBreadcrumb[] = api + .getBreadcrumbs(matches) + .map((breadcrumb, index, array) => { + return { + text: breadcrumb.title, + ...(index === array.length - 1 + ? {} + : { + href: breadcrumb.href, + }), + }; + }); + + useBreadcrumbs(formattedBreadcrumbs); + + return ( + + {children} + + ); +} diff --git a/x-pack/plugins/apm/public/context/breadcrumbs/use_breadcrumb.ts b/x-pack/plugins/apm/public/context/breadcrumbs/use_breadcrumb.ts new file mode 100644 index 0000000000000..dfc33c0f10ffc --- /dev/null +++ b/x-pack/plugins/apm/public/context/breadcrumbs/use_breadcrumb.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useCurrentRoute } from '@kbn/typed-react-router-config'; +import { useContext, useEffect, useRef } from 'react'; +import { castArray } from 'lodash'; +import { Breadcrumb, BreadcrumbsContext } from './context'; + +export function useBreadcrumb(breadcrumb: Breadcrumb | Breadcrumb[]) { + const api = useContext(BreadcrumbsContext); + + if (!api) { + throw new Error('Missing Breadcrumb API in context'); + } + + const { match } = useCurrentRoute(); + + const matchedRoute = useRef(match?.route); + + if (matchedRoute.current && matchedRoute.current !== match?.route) { + api.unset(matchedRoute.current); + } + + matchedRoute.current = match?.route; + + if (matchedRoute.current) { + api.set(matchedRoute.current, castArray(breadcrumb)); + } + + useEffect(() => { + return () => { + if (matchedRoute.current) { + api.unset(matchedRoute.current); + } + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); +} diff --git a/x-pack/plugins/apm/public/hooks/use_apm_breadcrumbs.test.tsx b/x-pack/plugins/apm/public/hooks/use_apm_breadcrumbs.test.tsx deleted file mode 100644 index 1cdb84c324750..0000000000000 --- a/x-pack/plugins/apm/public/hooks/use_apm_breadcrumbs.test.tsx +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { renderHook } from '@testing-library/react-hooks'; -import produce from 'immer'; -import React, { ReactNode } from 'react'; -import { MemoryRouter } from 'react-router-dom'; -import { apmRouteConfig } from '../components/routing/apm_route_config'; -import { ApmPluginContextValue } from '../context/apm_plugin/apm_plugin_context'; -import { - mockApmPluginContextValue, - MockApmPluginContextWrapper, -} from '../context/apm_plugin/mock_apm_plugin_context'; -import { useApmBreadcrumbs } from './use_apm_breadcrumbs'; -import { useBreadcrumbs } from '../../../observability/public'; - -jest.mock('../../../observability/public'); - -function createWrapper(path: string) { - return ({ children }: { children?: ReactNode }) => { - const value = (produce(mockApmPluginContextValue, (draft) => { - draft.core.application.navigateToUrl = (url: string) => Promise.resolve(); - }) as unknown) as ApmPluginContextValue; - - return ( - - - {children} - - - ); - }; -} - -function mountBreadcrumb(path: string) { - renderHook(() => useApmBreadcrumbs(apmRouteConfig), { - wrapper: createWrapper(path), - }); -} - -describe('useApmBreadcrumbs', () => { - test('/services/:serviceName/errors/:groupId', () => { - mountBreadcrumb( - '/services/opbeans-node/errors/myGroupId?kuery=myKuery&rangeFrom=now-24h&rangeTo=now&refreshPaused=true&refreshInterval=0' - ); - - expect(useBreadcrumbs).toHaveBeenCalledWith( - expect.arrayContaining([ - expect.objectContaining({ - text: 'APM', - href: - '/basepath/app/apm/?kuery=myKuery&rangeFrom=now-24h&rangeTo=now&refreshPaused=true&refreshInterval=0', - }), - expect.objectContaining({ - text: 'Services', - href: - '/basepath/app/apm/services?kuery=myKuery&rangeFrom=now-24h&rangeTo=now&refreshPaused=true&refreshInterval=0', - }), - expect.objectContaining({ - text: 'opbeans-node', - href: - '/basepath/app/apm/services/opbeans-node?kuery=myKuery&rangeFrom=now-24h&rangeTo=now&refreshPaused=true&refreshInterval=0', - }), - expect.objectContaining({ - text: 'Errors', - href: - '/basepath/app/apm/services/opbeans-node/errors?kuery=myKuery&rangeFrom=now-24h&rangeTo=now&refreshPaused=true&refreshInterval=0', - }), - expect.objectContaining({ text: 'myGroupId', href: undefined }), - ]) - ); - }); - - test('/services/:serviceName/errors', () => { - mountBreadcrumb('/services/opbeans-node/errors?kuery=myKuery'); - - expect(useBreadcrumbs).toHaveBeenCalledWith( - expect.arrayContaining([ - expect.objectContaining({ - text: 'APM', - href: '/basepath/app/apm/?kuery=myKuery', - }), - expect.objectContaining({ - text: 'Services', - href: '/basepath/app/apm/services?kuery=myKuery', - }), - expect.objectContaining({ - text: 'opbeans-node', - href: '/basepath/app/apm/services/opbeans-node?kuery=myKuery', - }), - expect.objectContaining({ text: 'Errors', href: undefined }), - ]) - ); - }); - - test('/services/:serviceName/transactions', () => { - mountBreadcrumb('/services/opbeans-node/transactions?kuery=myKuery'); - - expect(useBreadcrumbs).toHaveBeenCalledWith( - expect.arrayContaining([ - expect.objectContaining({ - text: 'APM', - href: '/basepath/app/apm/?kuery=myKuery', - }), - expect.objectContaining({ - text: 'Services', - href: '/basepath/app/apm/services?kuery=myKuery', - }), - expect.objectContaining({ - text: 'opbeans-node', - href: '/basepath/app/apm/services/opbeans-node?kuery=myKuery', - }), - expect.objectContaining({ text: 'Transactions', href: undefined }), - ]) - ); - }); - - test('/services/:serviceName/transactions/view?transactionName=my-transaction-name', () => { - mountBreadcrumb( - '/services/opbeans-node/transactions/view?kuery=myKuery&transactionName=my-transaction-name' - ); - - expect(useBreadcrumbs).toHaveBeenCalledWith( - expect.arrayContaining([ - expect.objectContaining({ - text: 'APM', - href: '/basepath/app/apm/?kuery=myKuery', - }), - expect.objectContaining({ - text: 'Services', - href: '/basepath/app/apm/services?kuery=myKuery', - }), - expect.objectContaining({ - text: 'opbeans-node', - href: '/basepath/app/apm/services/opbeans-node?kuery=myKuery', - }), - expect.objectContaining({ - text: 'Transactions', - href: - '/basepath/app/apm/services/opbeans-node/transactions?kuery=myKuery', - }), - expect.objectContaining({ - text: 'my-transaction-name', - href: undefined, - }), - ]) - ); - }); -}); diff --git a/x-pack/plugins/apm/public/hooks/use_apm_breadcrumbs.ts b/x-pack/plugins/apm/public/hooks/use_apm_breadcrumbs.ts deleted file mode 100644 index d64bcadf79577..0000000000000 --- a/x-pack/plugins/apm/public/hooks/use_apm_breadcrumbs.ts +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { History, Location } from 'history'; -import { ChromeBreadcrumb } from 'kibana/public'; -import { MouseEvent } from 'react'; -import { - match as Match, - matchPath, - RouteComponentProps, - useHistory, - useLocation, -} from 'react-router-dom'; -import { useBreadcrumbs } from '../../../observability/public'; -import { APMRouteDefinition, BreadcrumbTitle } from '../application/routes'; -import { getAPMHref } from '../components/shared/Links/apm/APMLink'; -import { useApmPluginContext } from '../context/apm_plugin/use_apm_plugin_context'; - -interface BreadcrumbWithoutLink extends ChromeBreadcrumb { - match: Match>; -} - -interface BreadcrumbFunctionArgs extends RouteComponentProps { - breadcrumbTitle: BreadcrumbTitle; -} - -/** - * Call the breadcrumb function if there is one, otherwise return it as a string - */ -function getBreadcrumbText({ - breadcrumbTitle, - history, - location, - match, -}: BreadcrumbFunctionArgs) { - return typeof breadcrumbTitle === 'function' - ? breadcrumbTitle({ history, location, match }) - : breadcrumbTitle; -} - -/** - * Get a breadcrumb from the current path and route definitions. - */ -function getBreadcrumb({ - currentPath, - history, - location, - routes, -}: { - currentPath: string; - history: History; - location: Location; - routes: APMRouteDefinition[]; -}) { - return routes.reduce( - (found, { breadcrumb, ...routeDefinition }) => { - if (found) { - return found; - } - - if (!breadcrumb) { - return null; - } - - const match = matchPath>( - currentPath, - routeDefinition - ); - - if (match) { - return { - match, - text: getBreadcrumbText({ - breadcrumbTitle: breadcrumb, - history, - location, - match, - }), - }; - } - - return null; - }, - null - ); -} - -/** - * Once we have the breadcrumbs, we need to iterate through the list again to - * add the href and onClick, since we need to know which one is the final - * breadcrumb - */ -function addLinksToBreadcrumbs({ - breadcrumbs, - navigateToUrl, - wrappedGetAPMHref, -}: { - breadcrumbs: BreadcrumbWithoutLink[]; - navigateToUrl: (url: string) => Promise; - wrappedGetAPMHref: (path: string) => string; -}) { - return breadcrumbs.map((breadcrumb, index) => { - const isLastBreadcrumbItem = index === breadcrumbs.length - 1; - - // Make the link not clickable if it's the last item - const href = isLastBreadcrumbItem - ? undefined - : wrappedGetAPMHref(breadcrumb.match.url); - const onClick = !href - ? undefined - : (event: MouseEvent) => { - event.preventDefault(); - navigateToUrl(href); - }; - - return { - ...breadcrumb, - match: undefined, - href, - onClick, - }; - }); -} - -/** - * Convert a list of route definitions to a list of breadcrumbs - */ -function routeDefinitionsToBreadcrumbs({ - history, - location, - routes, -}: { - history: History; - location: Location; - routes: APMRouteDefinition[]; -}) { - const breadcrumbs: BreadcrumbWithoutLink[] = []; - const { pathname } = location; - - pathname - .split('?')[0] - .replace(/\/$/, '') - .split('/') - .reduce((acc, next) => { - // `/1/2/3` results in match checks for `/1`, `/1/2`, `/1/2/3`. - const currentPath = !next ? '/' : `${acc}/${next}`; - const breadcrumb = getBreadcrumb({ - currentPath, - history, - location, - routes, - }); - - if (breadcrumb) { - breadcrumbs.push(breadcrumb); - } - - return currentPath === '/' ? '' : currentPath; - }, ''); - - return breadcrumbs; -} - -/** - * Determine the breadcrumbs from the routes, set them, and update the page - * title when the route changes. - */ -export function useApmBreadcrumbs(routes: APMRouteDefinition[]) { - const history = useHistory(); - const location = useLocation(); - const { search } = location; - const { core } = useApmPluginContext(); - const { basePath } = core.http; - const { navigateToUrl } = core.application; - - function wrappedGetAPMHref(path: string) { - return getAPMHref({ basePath, path, search }); - } - - const breadcrumbsWithoutLinks = routeDefinitionsToBreadcrumbs({ - history, - location, - routes, - }); - const breadcrumbs = addLinksToBreadcrumbs({ - breadcrumbs: breadcrumbsWithoutLinks, - wrappedGetAPMHref, - navigateToUrl, - }); - - useBreadcrumbs(breadcrumbs); -} diff --git a/x-pack/plugins/apm/public/hooks/use_apm_params.ts b/x-pack/plugins/apm/public/hooks/use_apm_params.ts new file mode 100644 index 0000000000000..d7661dbcf4d21 --- /dev/null +++ b/x-pack/plugins/apm/public/hooks/use_apm_params.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { OutputOf, PathsOf, useParams } from '@kbn/typed-react-router-config'; +import { ApmRoutes } from '../components/routing/apm_route_config'; + +export function useApmParams>( + path: TPath +): OutputOf { + return useParams(path as never); +} diff --git a/x-pack/plugins/apm/public/hooks/use_apm_router.ts b/x-pack/plugins/apm/public/hooks/use_apm_router.ts new file mode 100644 index 0000000000000..c0ccc37cc897d --- /dev/null +++ b/x-pack/plugins/apm/public/hooks/use_apm_router.ts @@ -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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useRouter } from '@kbn/typed-react-router-config'; +import type { ApmRouter } from '../components/routing/apm_route_config'; +import { useApmPluginContext } from '../context/apm_plugin/use_apm_plugin_context'; + +export function useApmRouter() { + const router = useRouter(); + const { core } = useApmPluginContext(); + + const link = (...args: any[]) => { + // a little too much effort needed to satisfy TS here + // @ts-ignore + return core.http.basePath.prepend('/app/apm' + router.link(...args)); + }; + + return ({ + ...router, + link, + } as unknown) as ApmRouter; +} diff --git a/x-pack/plugins/apm/public/hooks/use_service_metric_charts_fetcher.ts b/x-pack/plugins/apm/public/hooks/use_service_metric_charts_fetcher.ts index baf3eb51ae033..37eca08225e8f 100644 --- a/x-pack/plugins/apm/public/hooks/use_service_metric_charts_fetcher.ts +++ b/x-pack/plugins/apm/public/hooks/use_service_metric_charts_fetcher.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { useParams } from 'react-router-dom'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { MetricsChartsByAgentAPIResponse } from '../../server/lib/metrics/get_metrics_chart_data_by_agent'; import { useUrlParams } from '../context/url_params_context/use_url_params'; @@ -24,8 +23,7 @@ export function useServiceMetricChartsFetcher({ const { urlParams: { environment, kuery, start, end }, } = useUrlParams(); - const { agentName } = useApmServiceContext(); - const { serviceName } = useParams<{ serviceName?: string }>(); + const { agentName, serviceName } = useApmServiceContext(); const { data = INITIAL_DATA, error, status } = useFetcher( (callApmApi) => { diff --git a/x-pack/plugins/apm/public/hooks/use_service_name.tsx b/x-pack/plugins/apm/public/hooks/use_service_name.tsx index c003bf5223a32..5e2678374c68e 100644 --- a/x-pack/plugins/apm/public/hooks/use_service_name.tsx +++ b/x-pack/plugins/apm/public/hooks/use_service_name.tsx @@ -5,12 +5,10 @@ * 2.0. */ -import { useRouteMatch } from 'react-router-dom'; +import { useApmParams } from './use_apm_params'; export function useServiceName(): string | undefined { - const match = useRouteMatch<{ serviceName?: string }>( - '/services/:serviceName' - ); + const { path } = useApmParams('/*'); - return match ? match.params.serviceName : undefined; + return 'serviceName' in path ? path.serviceName : undefined; } diff --git a/x-pack/plugins/apm/public/hooks/use_transaction_distribution_fetcher.ts b/x-pack/plugins/apm/public/hooks/use_transaction_distribution_fetcher.ts index 25632d4b19cf4..8e48f386772b3 100644 --- a/x-pack/plugins/apm/public/hooks/use_transaction_distribution_fetcher.ts +++ b/x-pack/plugins/apm/public/hooks/use_transaction_distribution_fetcher.ts @@ -6,12 +6,13 @@ */ import { flatten, omit, isEmpty } from 'lodash'; -import { useHistory, useParams } from 'react-router-dom'; +import { useHistory } from 'react-router-dom'; import { useFetcher } from './use_fetcher'; import { toQuery, fromQuery } from '../components/shared/Links/url_helpers'; import { maybe } from '../../common/utils/maybe'; import { APIReturnType } from '../services/rest/createCallApmApi'; import { useUrlParams } from '../context/url_params_context/use_url_params'; +import { useApmServiceContext } from '../context/apm_service/use_apm_service_context'; type APIResponse = APIReturnType<'GET /api/apm/services/{serviceName}/transactions/charts/distribution'>; @@ -21,19 +22,15 @@ const INITIAL_DATA = { bucketSize: 0, }; -export function useTransactionDistributionFetcher() { - const { serviceName } = useParams<{ serviceName?: string }>(); +export function useTransactionDistributionFetcher({ + transactionName, +}: { + transactionName: string; +}) { + const { serviceName, transactionType } = useApmServiceContext(); + const { - urlParams: { - environment, - kuery, - start, - end, - transactionType, - transactionId, - traceId, - transactionName, - }, + urlParams: { environment, kuery, start, end, transactionId, traceId }, } = useUrlParams(); const history = useHistory(); diff --git a/x-pack/plugins/apm/public/hooks/use_transaction_latency_chart_fetcher.ts b/x-pack/plugins/apm/public/hooks/use_transaction_latency_chart_fetcher.ts index 0f1592ca2679f..5ae4a138608ec 100644 --- a/x-pack/plugins/apm/public/hooks/use_transaction_latency_chart_fetcher.ts +++ b/x-pack/plugins/apm/public/hooks/use_transaction_latency_chart_fetcher.ts @@ -6,7 +6,6 @@ */ import { useMemo } from 'react'; -import { useParams } from 'react-router-dom'; import { useFetcher } from './use_fetcher'; import { useUrlParams } from '../context/url_params_context/use_url_params'; import { useApmServiceContext } from '../context/apm_service/use_apm_service_context'; @@ -15,8 +14,7 @@ import { useTheme } from './use_theme'; import { getTimeRangeComparison } from '../components/shared/time_comparison/get_time_range_comparison'; export function useTransactionLatencyChartsFetcher() { - const { serviceName } = useParams<{ serviceName?: string }>(); - const { transactionType } = useApmServiceContext(); + const { transactionType, serviceName } = useApmServiceContext(); const theme = useTheme(); const { urlParams: { diff --git a/x-pack/plugins/apm/public/hooks/use_transaction_throughput_chart_fetcher.ts b/x-pack/plugins/apm/public/hooks/use_transaction_throughput_chart_fetcher.ts index c8ae4fa5823a4..72e469178a100 100644 --- a/x-pack/plugins/apm/public/hooks/use_transaction_throughput_chart_fetcher.ts +++ b/x-pack/plugins/apm/public/hooks/use_transaction_throughput_chart_fetcher.ts @@ -6,7 +6,6 @@ */ import { useMemo } from 'react'; -import { useParams } from 'react-router-dom'; import { useFetcher } from './use_fetcher'; import { useUrlParams } from '../context/url_params_context/use_url_params'; import { getThroughputChartSelector } from '../selectors/throughput_chart_selectors'; @@ -14,8 +13,7 @@ import { useTheme } from './use_theme'; import { useApmServiceContext } from '../context/apm_service/use_apm_service_context'; export function useTransactionThroughputChartsFetcher() { - const { serviceName } = useParams<{ serviceName?: string }>(); - const { transactionType } = useApmServiceContext(); + const { transactionType, serviceName } = useApmServiceContext(); const theme = useTheme(); const { urlParams: { environment, kuery, start, end, transactionName }, diff --git a/x-pack/plugins/apm/public/utils/testHelpers.tsx b/x-pack/plugins/apm/public/utils/testHelpers.tsx index 9a1d4da8ece7c..465155dbf166b 100644 --- a/x-pack/plugins/apm/public/utils/testHelpers.tsx +++ b/x-pack/plugins/apm/public/utils/testHelpers.tsx @@ -67,13 +67,13 @@ export function mockMoment() { // Useful for getting the rendered href from any kind of link component export async function getRenderedHref(Component: React.FC, location: Location) { const el = render( - - + + - - + + ); const a = el.container.querySelector('a'); diff --git a/x-pack/plugins/apm/scripts/precommit.js b/x-pack/plugins/apm/scripts/precommit.js index 88d2e169dd542..89c5055c6a7f7 100644 --- a/x-pack/plugins/apm/scripts/precommit.js +++ b/x-pack/plugins/apm/scripts/precommit.js @@ -71,6 +71,8 @@ const tasks = new Listr( resolve(__dirname, '../../../../node_modules/jest-silent-reporter'), '--collect-coverage', 'false', + '--maxWorkers', + 4, ], execaOpts ), diff --git a/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_duration.ts b/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_duration.ts index 6ce175fcb8362..3a67076201efa 100644 --- a/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_duration.ts +++ b/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_duration.ts @@ -13,7 +13,8 @@ import { TRANSACTION_TYPE, } from '../../../../common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../../common/processor_event'; -import { environmentQuery, rangeQuery } from '../../../../server/utils/queries'; +import { rangeQuery } from '../../../../../observability/server'; +import { environmentQuery } from '../../../../common/utils/environment_query'; import { AlertParams } from '../../../routes/alerts/chart_preview'; import { getBucketSize } from '../../helpers/get_bucket_size'; import { Setup, SetupTimeRange } from '../../helpers/setup_request'; diff --git a/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_error_count.ts b/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_error_count.ts index 3d64c63cb2041..0ead50c709083 100644 --- a/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_error_count.ts +++ b/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_error_count.ts @@ -8,7 +8,8 @@ import { SERVICE_NAME } from '../../../../common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../../common/processor_event'; import { AlertParams } from '../../../routes/alerts/chart_preview'; -import { environmentQuery, rangeQuery } from '../../../../server/utils/queries'; +import { rangeQuery } from '../../../../../observability/server'; +import { environmentQuery } from '../../../../common/utils/environment_query'; import { getBucketSize } from '../../helpers/get_bucket_size'; import { Setup, SetupTimeRange } from '../../helpers/setup_request'; diff --git a/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_error_rate.ts b/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_error_rate.ts index 0a6a25ad9c533..888c929a2c721 100644 --- a/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_error_rate.ts +++ b/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_error_rate.ts @@ -12,7 +12,8 @@ import { } from '../../../../common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../../common/processor_event'; import { AlertParams } from '../../../routes/alerts/chart_preview'; -import { environmentQuery, rangeQuery } from '../../../../server/utils/queries'; +import { rangeQuery } from '../../../../../observability/server'; +import { environmentQuery } from '../../../../common/utils/environment_query'; import { getBucketSize } from '../../helpers/get_bucket_size'; import { Setup, SetupTimeRange } from '../../helpers/setup_request'; import { diff --git a/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.ts b/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.ts index 35c80df2ca31c..bdd6b240c4bbc 100644 --- a/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.ts +++ b/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.ts @@ -28,7 +28,7 @@ import { SERVICE_NAME, } from '../../../common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../common/processor_event'; -import { environmentQuery } from '../../../server/utils/queries'; +import { environmentQuery } from '../../../common/utils/environment_query'; import { getApmIndices } from '../settings/apm_indices/get_apm_indices'; import { apmActionVariables } from './action_variables'; import { alertingEsClient } from './alerting_es_client'; diff --git a/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts b/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts index ff202669fe1da..c14675cb93987 100644 --- a/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts +++ b/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts @@ -30,7 +30,7 @@ import { } from '../../../common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../common/processor_event'; import { getDurationFormatter } from '../../../common/utils/formatters'; -import { environmentQuery } from '../../../server/utils/queries'; +import { environmentQuery } from '../../../common/utils/environment_query'; import { getApmIndices } from '../settings/apm_indices/get_apm_indices'; import { apmActionVariables } from './action_variables'; import { alertingEsClient } from './alerting_es_client'; diff --git a/x-pack/plugins/apm/server/lib/alerts/register_transaction_error_rate_alert_type.ts b/x-pack/plugins/apm/server/lib/alerts/register_transaction_error_rate_alert_type.ts index 36fd9c3fac58d..a397302659538 100644 --- a/x-pack/plugins/apm/server/lib/alerts/register_transaction_error_rate_alert_type.ts +++ b/x-pack/plugins/apm/server/lib/alerts/register_transaction_error_rate_alert_type.ts @@ -32,7 +32,7 @@ import { import { EventOutcome } from '../../../common/event_outcome'; import { ProcessorEvent } from '../../../common/processor_event'; import { asDecimalOrInteger } from '../../../common/utils/formatters'; -import { environmentQuery } from '../../../server/utils/queries'; +import { environmentQuery } from '../../../common/utils/environment_query'; import { getApmIndices } from '../settings/apm_indices/get_apm_indices'; import { apmActionVariables } from './action_variables'; import { alertingEsClient } from './alerting_es_client'; diff --git a/x-pack/plugins/apm/server/lib/alerts/test_utils/index.ts b/x-pack/plugins/apm/server/lib/alerts/test_utils/index.ts index 1366503ea1428..679f33707b5b5 100644 --- a/x-pack/plugins/apm/server/lib/alerts/test_utils/index.ts +++ b/x-pack/plugins/apm/server/lib/alerts/test_utils/index.ts @@ -70,8 +70,14 @@ export const createRuleTypeMocks = () => { executor: async ({ params }: { params: Record }) => { return alertExecutor({ services, - rule: { consumer: APM_SERVER_FEATURE_ID }, params, + rule: { + consumer: APM_SERVER_FEATURE_ID, + name: 'name', + producer: 'producer', + ruleTypeId: 'ruleTypeId', + ruleTypeName: 'ruleTypeName', + }, startedAt: new Date(), }); }, diff --git a/x-pack/plugins/apm/server/lib/anomaly_detection/create_anomaly_detection_jobs.ts b/x-pack/plugins/apm/server/lib/anomaly_detection/create_anomaly_detection_jobs.ts index d93514c42bbdd..28f3041d65d70 100644 --- a/x-pack/plugins/apm/server/lib/anomaly_detection/create_anomaly_detection_jobs.ts +++ b/x-pack/plugins/apm/server/lib/anomaly_detection/create_anomaly_detection_jobs.ts @@ -11,7 +11,7 @@ import { snakeCase } from 'lodash'; import Boom from '@hapi/boom'; import { ML_ERRORS } from '../../../common/anomaly_detection'; import { ProcessorEvent } from '../../../common/processor_event'; -import { environmentQuery } from '../../../server/utils/queries'; +import { environmentQuery } from '../../../common/utils/environment_query'; import { Setup } from '../helpers/setup_request'; import { TRANSACTION_DURATION, diff --git a/x-pack/plugins/apm/server/lib/correlations/get_filters.ts b/x-pack/plugins/apm/server/lib/correlations/get_filters.ts index 61fec492ad38e..d6e8f3f57c91a 100644 --- a/x-pack/plugins/apm/server/lib/correlations/get_filters.ts +++ b/x-pack/plugins/apm/server/lib/correlations/get_filters.ts @@ -7,7 +7,8 @@ import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { ESFilter } from '../../../../../../src/core/types/elasticsearch'; -import { environmentQuery, rangeQuery, kqlQuery } from '../../utils/queries'; +import { rangeQuery, kqlQuery } from '../../../../observability/server'; +import { environmentQuery } from '../../../common/utils/environment_query'; import { SERVICE_NAME, TRANSACTION_NAME, diff --git a/x-pack/plugins/apm/server/lib/environments/get_environments.ts b/x-pack/plugins/apm/server/lib/environments/get_environments.ts index c0b267f180010..84448b1c7c2b1 100644 --- a/x-pack/plugins/apm/server/lib/environments/get_environments.ts +++ b/x-pack/plugins/apm/server/lib/environments/get_environments.ts @@ -11,7 +11,7 @@ import { } from '../../../common/elasticsearch_fieldnames'; import { ENVIRONMENT_NOT_DEFINED } from '../../../common/environment_filter_values'; import { ProcessorEvent } from '../../../common/processor_event'; -import { rangeQuery } from '../../../server/utils/queries'; +import { rangeQuery } from '../../../../observability/server'; import { getProcessorEventForAggregatedTransactions } from '../helpers/aggregated_transactions'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; diff --git a/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts b/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts index fa73ce8f2bc85..48bdfd84b0443 100644 --- a/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts +++ b/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts @@ -11,11 +11,8 @@ import { SERVICE_NAME, } from '../../../../common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../../common/processor_event'; -import { - environmentQuery, - rangeQuery, - kqlQuery, -} from '../../../../server/utils/queries'; +import { rangeQuery, kqlQuery } from '../../../../../observability/server'; +import { environmentQuery } from '../../../../common/utils/environment_query'; import { Setup, SetupTimeRange } from '../../helpers/setup_request'; export async function getBuckets({ diff --git a/x-pack/plugins/apm/server/lib/errors/get_error_group_sample.ts b/x-pack/plugins/apm/server/lib/errors/get_error_group_sample.ts index a915a4fb03305..1a05f55dc8def 100644 --- a/x-pack/plugins/apm/server/lib/errors/get_error_group_sample.ts +++ b/x-pack/plugins/apm/server/lib/errors/get_error_group_sample.ts @@ -12,11 +12,8 @@ import { TRANSACTION_SAMPLED, } from '../../../common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../common/processor_event'; -import { - environmentQuery, - rangeQuery, - kqlQuery, -} from '../../../server/utils/queries'; +import { rangeQuery, kqlQuery } from '../../../../observability/server'; +import { environmentQuery } from '../../../common/utils/environment_query'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { getTransaction } from '../transactions/get_transaction'; diff --git a/x-pack/plugins/apm/server/lib/fleet/create_cloud_apm_package_policy.ts b/x-pack/plugins/apm/server/lib/fleet/create_cloud_apm_package_policy.ts index c336e5dc95ba6..6726865c6c3a5 100644 --- a/x-pack/plugins/apm/server/lib/fleet/create_cloud_apm_package_policy.ts +++ b/x-pack/plugins/apm/server/lib/fleet/create_cloud_apm_package_policy.ts @@ -10,6 +10,7 @@ import { SavedObjectsClientContract, Logger, } from 'kibana/server'; +import { PackagePolicy } from '../../../../fleet/common'; import { APM_SERVER_SCHEMA_SAVED_OBJECT_TYPE, APM_SERVER_SCHEMA_SAVED_OBJECT_ID, @@ -19,6 +20,8 @@ import { APMPluginStartDependencies, } from '../../types'; import { getApmPackagePolicyDefinition } from './get_apm_package_policy_definition'; +import { Setup } from '../helpers/setup_request'; +import { mergePackagePolicyWithApm } from './merge_package_policy_with_apm'; export async function createCloudApmPackgePolicy({ cloudPluginSetup, @@ -26,13 +29,15 @@ export async function createCloudApmPackgePolicy({ savedObjectsClient, esClient, logger, + setup, }: { cloudPluginSetup: APMPluginSetupDependencies['cloud']; fleetPluginStart: NonNullable; savedObjectsClient: SavedObjectsClientContract; esClient: ElasticsearchClient; logger: Logger; -}) { + setup: Setup; +}): Promise { const { attributes } = await savedObjectsClient.get( APM_SERVER_SCHEMA_SAVED_OBJECT_TYPE, APM_SERVER_SCHEMA_SAVED_OBJECT_ID @@ -40,15 +45,21 @@ export async function createCloudApmPackgePolicy({ const apmServerSchema: Record = JSON.parse( (attributes as { schemaJson: string }).schemaJson ); + // Merges agent config and source maps with the new APM cloud package policy const apmPackagePolicyDefinition = getApmPackagePolicyDefinition({ apmServerSchema, cloudPluginSetup, }); + const mergedAPMPackagePolicy = await mergePackagePolicyWithApm({ + setup, + packagePolicy: apmPackagePolicyDefinition, + fleetPluginStart, + }); logger.info(`Fleet migration on Cloud - apmPackagePolicy create start`); const apmPackagePolicy = await fleetPluginStart.packagePolicyService.create( savedObjectsClient, esClient, - apmPackagePolicyDefinition, + mergedAPMPackagePolicy, { force: true, bumpRevision: true } ); logger.info(`Fleet migration on Cloud - apmPackagePolicy create end`); diff --git a/x-pack/plugins/apm/server/lib/fleet/merge_package_policy_with_apm.ts b/x-pack/plugins/apm/server/lib/fleet/merge_package_policy_with_apm.ts new file mode 100644 index 0000000000000..fd3e3db700fd0 --- /dev/null +++ b/x-pack/plugins/apm/server/lib/fleet/merge_package_policy_with_apm.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Setup } from '../helpers/setup_request'; +import { APMPluginStartDependencies } from '../../types'; +import { listConfigurations } from '../settings/agent_configuration/list_configurations'; +import { + getPackagePolicyWithAgentConfigurations, + PackagePolicy, +} from './register_fleet_policy_callbacks'; +import { getPackagePolicyWithSourceMap, listArtifacts } from './source_maps'; + +export async function mergePackagePolicyWithApm({ + packagePolicy, + setup, + fleetPluginStart, +}: { + packagePolicy: PackagePolicy; + setup: Setup; + fleetPluginStart: NonNullable; +}) { + const agentConfigurations = await listConfigurations({ setup }); + const artifacts = await listArtifacts({ fleetPluginStart }); + return getPackagePolicyWithAgentConfigurations( + getPackagePolicyWithSourceMap({ packagePolicy, artifacts }), + agentConfigurations + ); +} diff --git a/x-pack/plugins/apm/server/lib/fleet/register_fleet_policy_callbacks.ts b/x-pack/plugins/apm/server/lib/fleet/register_fleet_policy_callbacks.ts index c122a5c406eab..b0065b70673a5 100644 --- a/x-pack/plugins/apm/server/lib/fleet/register_fleet_policy_callbacks.ts +++ b/x-pack/plugins/apm/server/lib/fleet/register_fleet_policy_callbacks.ts @@ -6,13 +6,12 @@ */ import { APMPlugin, APMRouteHandlerResources } from '../..'; -import { listConfigurations } from '../settings/agent_configuration/list_configurations'; -import { setupRequest } from '../helpers/setup_request'; -import { APMPluginStartDependencies } from '../../types'; import { ExternalCallback } from '../../../../fleet/server'; -import { AGENT_NAME } from '../../../common/elasticsearch_fieldnames'; import { AgentConfiguration } from '../../../common/agent_configuration/configuration_types'; -import { getPackagePolicyWithSourceMap, listArtifacts } from './source_maps'; +import { AGENT_NAME } from '../../../common/elasticsearch_fieldnames'; +import { APMPluginStartDependencies } from '../../types'; +import { setupRequest } from '../helpers/setup_request'; +import { mergePackagePolicyWithApm } from './merge_package_policy_with_apm'; export async function registerFleetPolicyCallbacks({ plugins, @@ -91,12 +90,11 @@ function registerPackagePolicyExternalCallback({ logger, ruleDataClient, }); - const agentConfigurations = await listConfigurations({ setup }); - const artifacts = await listArtifacts({ fleetPluginStart }); - return getPackagePolicyWithAgentConfigurations( - getPackagePolicyWithSourceMap({ packagePolicy, artifacts }), - agentConfigurations - ); + return await mergePackagePolicyWithApm({ + setup, + fleetPluginStart, + packagePolicy, + }); }; fleetPluginStart.registerExternalCallback(callbackName, callbackFn); diff --git a/x-pack/plugins/apm/server/lib/fleet/sync_agent_configs_to_apm_package_policies.ts b/x-pack/plugins/apm/server/lib/fleet/sync_agent_configs_to_apm_package_policies.ts index 4294c5b82cd63..1365ddc28ddb2 100644 --- a/x-pack/plugins/apm/server/lib/fleet/sync_agent_configs_to_apm_package_policies.ts +++ b/x-pack/plugins/apm/server/lib/fleet/sync_agent_configs_to_apm_package_policies.ts @@ -10,6 +10,7 @@ import { CoreStart, SavedObjectsClientContract, } from 'kibana/server'; +import { TelemetryUsageCounter } from '../../routes/typings'; import { APMPluginStartDependencies } from '../../types'; import { getInternalSavedObjectsClient } from '../helpers/get_internal_saved_objects_client'; import { Setup } from '../helpers/setup_request'; @@ -21,11 +22,19 @@ export async function syncAgentConfigsToApmPackagePolicies({ core, fleetPluginStart, setup, + telemetryUsageCounter, }: { core: { setup: CoreSetup; start: () => Promise }; fleetPluginStart: NonNullable; setup: Setup; + telemetryUsageCounter?: TelemetryUsageCounter; }) { + if (telemetryUsageCounter) { + telemetryUsageCounter.incrementCounter({ + counterName: 'sync_agent_config_to_apm_package_policies', + counterType: 'success', + }); + } const coreStart = await core.start(); const esClient = coreStart.elasticsearch.client.asInternalUser; const [ diff --git a/x-pack/plugins/apm/server/lib/helpers/aggregated_transactions/index.ts b/x-pack/plugins/apm/server/lib/helpers/aggregated_transactions/index.ts index 60ce36a85235e..d1174fcfcac6c 100644 --- a/x-pack/plugins/apm/server/lib/helpers/aggregated_transactions/index.ts +++ b/x-pack/plugins/apm/server/lib/helpers/aggregated_transactions/index.ts @@ -6,7 +6,7 @@ */ import { SearchAggregatedTransactionSetting } from '../../../../common/aggregated_transactions'; -import { kqlQuery, rangeQuery } from '../../../../server/utils/queries'; +import { kqlQuery, rangeQuery } from '../../../../../observability/server'; import { ProcessorEvent } from '../../../../common/processor_event'; import { TRANSACTION_DURATION, diff --git a/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.test.ts b/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.test.ts index a5340c1220b44..ef869a0ed6cfa 100644 --- a/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.test.ts +++ b/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.test.ts @@ -13,6 +13,11 @@ import { APMConfig } from '../..'; function getMockSavedObjectsClient() { return ({ + get: jest.fn(() => ({ + attributes: { + title: 'apm-*', + }, + })), create: jest.fn(), } as unknown) as InternalSavedObjectsClient; } @@ -22,14 +27,12 @@ describe('createStaticIndexPattern', () => { const setup = {} as Setup; const savedObjectsClient = getMockSavedObjectsClient(); - await createStaticIndexPattern( + await createStaticIndexPattern({ setup, - { - 'xpack.apm.autocreateApmIndexPattern': false, - } as APMConfig, + config: { 'xpack.apm.autocreateApmIndexPattern': false } as APMConfig, savedObjectsClient, - 'default' - ); + spaceId: 'default', + }); expect(savedObjectsClient.create).not.toHaveBeenCalled(); }); @@ -43,14 +46,12 @@ describe('createStaticIndexPattern', () => { const savedObjectsClient = getMockSavedObjectsClient(); - await createStaticIndexPattern( + await createStaticIndexPattern({ setup, - { - 'xpack.apm.autocreateApmIndexPattern': true, - } as APMConfig, + config: { 'xpack.apm.autocreateApmIndexPattern': true } as APMConfig, savedObjectsClient, - 'default' - ); + spaceId: 'default', + }); expect(savedObjectsClient.create).not.toHaveBeenCalled(); }); @@ -64,15 +65,73 @@ describe('createStaticIndexPattern', () => { const savedObjectsClient = getMockSavedObjectsClient(); - await createStaticIndexPattern( + await createStaticIndexPattern({ setup, - { + config: { 'xpack.apm.autocreateApmIndexPattern': true } as APMConfig, + savedObjectsClient, + spaceId: 'default', + }); + + expect(savedObjectsClient.create).toHaveBeenCalled(); + }); + + it(`should upgrade an index pattern if 'apm_oss.indexPattern' does not match title`, async () => { + const setup = {} as Setup; + + // does have APM data + jest + .spyOn(HistoricalAgentData, 'hasHistoricalAgentData') + .mockResolvedValue(true); + + const savedObjectsClient = getMockSavedObjectsClient(); + const apmIndexPatternTitle = 'traces-apm*,logs-apm*,metrics-apm*,apm-*'; + + await createStaticIndexPattern({ + setup, + config: { 'xpack.apm.autocreateApmIndexPattern': true, + // eslint-disable-next-line @typescript-eslint/naming-convention + 'apm_oss.indexPattern': apmIndexPatternTitle, } as APMConfig, savedObjectsClient, - 'default' + spaceId: 'default', + }); + + expect(savedObjectsClient.get).toHaveBeenCalled(); + expect(savedObjectsClient.create).toHaveBeenCalled(); + // @ts-ignore + expect(savedObjectsClient.create.mock.calls[0][1].title).toBe( + apmIndexPatternTitle ); + // @ts-ignore + expect(savedObjectsClient.create.mock.calls[0][2].overwrite).toBe(true); + }); + + it(`should not upgrade an index pattern if 'apm_oss.indexPattern' already match existing title`, async () => { + const setup = {} as Setup; + + // does have APM data + jest + .spyOn(HistoricalAgentData, 'hasHistoricalAgentData') + .mockResolvedValue(true); + + const savedObjectsClient = getMockSavedObjectsClient(); + const apmIndexPatternTitle = 'apm-*'; + + await createStaticIndexPattern({ + setup, + config: { + 'xpack.apm.autocreateApmIndexPattern': true, + // eslint-disable-next-line @typescript-eslint/naming-convention + 'apm_oss.indexPattern': apmIndexPatternTitle, + } as APMConfig, + savedObjectsClient, + spaceId: 'default', + }); + expect(savedObjectsClient.get).toHaveBeenCalled(); expect(savedObjectsClient.create).toHaveBeenCalled(); + // @ts-ignore + expect(savedObjectsClient.create.mock.calls[0][2].overwrite).toBe(false); }); }); diff --git a/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.ts b/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.ts index a2944d6241d2d..5dbee59b4ce86 100644 --- a/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.ts +++ b/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.ts @@ -15,13 +15,23 @@ import { InternalSavedObjectsClient } from '../helpers/get_internal_saved_object import { withApmSpan } from '../../utils/with_apm_span'; import { getApmIndexPatternTitle } from './get_apm_index_pattern_title'; -export async function createStaticIndexPattern( - setup: Setup, - config: APMRouteHandlerResources['config'], - savedObjectsClient: InternalSavedObjectsClient, - spaceId: string | undefined, - overwrite = false -): Promise { +type ApmIndexPatternAttributes = typeof apmIndexPattern.attributes & { + title: string; +}; + +export async function createStaticIndexPattern({ + setup, + config, + savedObjectsClient, + spaceId, + overwrite = false, +}: { + setup: Setup; + config: APMRouteHandlerResources['config']; + savedObjectsClient: InternalSavedObjectsClient; + spaceId?: string; + overwrite?: boolean; +}): Promise { return withApmSpan('create_static_index_pattern', async () => { // don't autocreate APM index pattern if it's been disabled via the config if (!config['xpack.apm.autocreateApmIndexPattern']) { @@ -35,8 +45,31 @@ export async function createStaticIndexPattern( return false; } + const apmIndexPatternTitle = getApmIndexPatternTitle(config); + + if (!overwrite) { + try { + const { + attributes: { title: existingApmIndexPatternTitle }, + }: { + attributes: ApmIndexPatternAttributes; + } = await savedObjectsClient.get( + 'index-pattern', + APM_STATIC_INDEX_PATTERN_ID + ); + // if the existing index pattern does not matches the new one, force an update + if (existingApmIndexPatternTitle !== apmIndexPatternTitle) { + overwrite = true; + } + } catch (e) { + // if the index pattern (saved object) is not found, then we can continue with creation + if (!SavedObjectsErrorHelpers.isNotFoundError(e)) { + throw e; + } + } + } + try { - const apmIndexPatternTitle = getApmIndexPatternTitle(config); await withApmSpan('create_index_pattern_saved_object', () => savedObjectsClient.create( 'index-pattern', diff --git a/x-pack/plugins/apm/server/lib/observability_overview/get_service_count.ts b/x-pack/plugins/apm/server/lib/observability_overview/get_service_count.ts index 086516371387e..7bd46bfaabdd4 100644 --- a/x-pack/plugins/apm/server/lib/observability_overview/get_service_count.ts +++ b/x-pack/plugins/apm/server/lib/observability_overview/get_service_count.ts @@ -6,7 +6,7 @@ */ import { ProcessorEvent } from '../../../common/processor_event'; -import { rangeQuery } from '../../../server/utils/queries'; +import { rangeQuery } from '../../../../observability/server'; import { SERVICE_NAME } from '../../../common/elasticsearch_fieldnames'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { getProcessorEventForAggregatedTransactions } from '../helpers/aggregated_transactions'; diff --git a/x-pack/plugins/apm/server/lib/observability_overview/get_transactions_per_minute.ts b/x-pack/plugins/apm/server/lib/observability_overview/get_transactions_per_minute.ts index 016cb50566da0..c74e910e8cd27 100644 --- a/x-pack/plugins/apm/server/lib/observability_overview/get_transactions_per_minute.ts +++ b/x-pack/plugins/apm/server/lib/observability_overview/get_transactions_per_minute.ts @@ -10,7 +10,7 @@ import { TRANSACTION_REQUEST, } from '../../../common/transaction_types'; import { TRANSACTION_TYPE } from '../../../common/elasticsearch_fieldnames'; -import { rangeQuery } from '../../../server/utils/queries'; +import { rangeQuery } from '../../../../observability/server'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { getProcessorEventForAggregatedTransactions } from '../helpers/aggregated_transactions'; import { calculateThroughput } from '../helpers/calculate_throughput'; diff --git a/x-pack/plugins/apm/server/lib/rum_client/has_rum_data.ts b/x-pack/plugins/apm/server/lib/rum_client/has_rum_data.ts index fc5da4ec1d0fa..28fab3369b1eb 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/has_rum_data.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/has_rum_data.ts @@ -11,7 +11,7 @@ import { TRANSACTION_TYPE, } from '../../../common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../common/processor_event'; -import { rangeQuery } from '../../../server/utils/queries'; +import { rangeQuery } from '../../../../observability/server'; import { TRANSACTION_PAGE_LOAD } from '../../../common/transaction_types'; export async function hasRumData({ diff --git a/x-pack/plugins/apm/server/lib/rum_client/ui_filters/get_es_filter.ts b/x-pack/plugins/apm/server/lib/rum_client/ui_filters/get_es_filter.ts index 48beb9bca5241..5f587f82e979d 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/ui_filters/get_es_filter.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/ui_filters/get_es_filter.ts @@ -11,7 +11,7 @@ import { } from '../../../../common/ux_ui_filter'; import { ESFilter } from '../../../../../../../src/core/types/elasticsearch'; import { UxUIFilters } from '../../../../typings/ui_filters'; -import { environmentQuery } from '../../../utils/queries'; +import { environmentQuery } from '../../../../common/utils/environment_query'; export function getEsFilter(uiFilters: UxUIFilters, exclude?: boolean) { const localFilterValues = uiFilters; diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/async_search_service.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/async_search_service.ts index 7a511fc60fd06..155cb1f4615bd 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/async_search_service.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/async_search_service.ts @@ -11,7 +11,7 @@ import { fetchTransactionDurationFieldCandidates } from './query_field_candidate import { fetchTransactionDurationFieldValuePairs } from './query_field_value_pairs'; import { fetchTransactionDurationPercentiles } from './query_percentiles'; import { fetchTransactionDurationCorrelation } from './query_correlation'; -import { fetchTransactionDurationHistogramRangesteps } from './query_histogram_rangesteps'; +import { fetchTransactionDurationHistogramRangeSteps } from './query_histogram_range_steps'; import { fetchTransactionDurationRanges, HistogramItem } from './query_ranges'; import type { AsyncSearchProviderProgress, @@ -24,6 +24,8 @@ import { fetchTransactionDurationFractions } from './query_fractions'; const CORRELATION_THRESHOLD = 0.3; const KS_TEST_THRESHOLD = 0.1; +const currentTimeAsString = () => new Date().toISOString(); + export const asyncSearchServiceProvider = ( esClient: ElasticsearchClient, params: SearchServiceParams @@ -31,6 +33,9 @@ export const asyncSearchServiceProvider = ( let isCancelled = false; let isRunning = true; let error: Error; + const log: string[] = []; + const logMessage = (message: string) => + log.push(`${currentTimeAsString()}: ${message}`); const progress: AsyncSearchProviderProgress = { started: Date.now(), @@ -53,13 +58,17 @@ export const asyncSearchServiceProvider = ( let percentileThresholdValue: number; const cancel = () => { + logMessage(`Service cancelled.`); isCancelled = true; }; const fetchCorrelations = async () => { try { // 95th percentile to be displayed as a marker in the log log chart - const percentileThreshold = await fetchTransactionDurationPercentiles( + const { + totalDocs, + percentiles: percentileThreshold, + } = await fetchTransactionDurationPercentiles( esClient, params, params.percentileThreshold ? [params.percentileThreshold] : undefined @@ -67,12 +76,32 @@ export const asyncSearchServiceProvider = ( percentileThresholdValue = percentileThreshold[`${params.percentileThreshold}.0`]; - const histogramRangeSteps = await fetchTransactionDurationHistogramRangesteps( + logMessage( + `Fetched ${params.percentileThreshold}th percentile value of ${percentileThresholdValue} based on ${totalDocs} documents.` + ); + + // finish early if we weren't able to identify the percentileThresholdValue. + if (percentileThresholdValue === undefined) { + logMessage( + `Abort service since percentileThresholdValue could not be determined.` + ); + progress.loadedHistogramStepsize = 1; + progress.loadedOverallHistogram = 1; + progress.loadedFieldCanditates = 1; + progress.loadedFieldValuePairs = 1; + progress.loadedHistograms = 1; + isRunning = false; + return; + } + + const histogramRangeSteps = await fetchTransactionDurationHistogramRangeSteps( esClient, params ); progress.loadedHistogramStepsize = 1; + logMessage(`Loaded histogram range steps.`); + if (isCancelled) { isRunning = false; return; @@ -86,6 +115,8 @@ export const asyncSearchServiceProvider = ( progress.loadedOverallHistogram = 1; overallHistogram = overallLogHistogramChartData; + logMessage(`Loaded overall histogram chart data.`); + if (isCancelled) { isRunning = false; return; @@ -93,13 +124,13 @@ export const asyncSearchServiceProvider = ( // Create an array of ranges [2, 4, 6, ..., 98] const percents = Array.from(range(2, 100, 2)); - const percentilesRecords = await fetchTransactionDurationPercentiles( - esClient, - params, - percents - ); + const { + percentiles: percentilesRecords, + } = await fetchTransactionDurationPercentiles(esClient, params, percents); const percentiles = Object.values(percentilesRecords); + logMessage(`Loaded percentiles.`); + if (isCancelled) { isRunning = false; return; @@ -110,6 +141,8 @@ export const asyncSearchServiceProvider = ( params ); + logMessage(`Identified ${fieldCandidates.length} fieldCandidates.`); + progress.loadedFieldCanditates = 1; const fieldValuePairs = await fetchTransactionDurationFieldValuePairs( @@ -119,6 +152,8 @@ export const asyncSearchServiceProvider = ( progress ); + logMessage(`Identified ${fieldValuePairs.length} fieldValuePairs.`); + if (isCancelled) { isRunning = false; return; @@ -133,6 +168,8 @@ export const asyncSearchServiceProvider = ( totalDocCount, } = await fetchTransactionDurationFractions(esClient, params, ranges); + logMessage(`Loaded fractions and totalDocCount of ${totalDocCount}.`); + async function* fetchTransactionDurationHistograms() { for (const item of shuffle(fieldValuePairs)) { if (item === undefined || isCancelled) { @@ -185,7 +222,11 @@ export const asyncSearchServiceProvider = ( yield undefined; } } catch (e) { - error = e; + // don't fail the whole process for individual correlation queries, just add the error to the internal log. + logMessage( + `Failed to fetch correlation/kstest for '${item.field}/${item.value}'` + ); + yield undefined; } } } @@ -199,10 +240,14 @@ export const asyncSearchServiceProvider = ( progress.loadedHistograms = loadedHistograms / fieldValuePairs.length; } - isRunning = false; + logMessage( + `Identified ${values.length} significant correlations out of ${fieldValuePairs.length} field/value pairs.` + ); } catch (e) { error = e; } + + isRunning = false; }; fetchCorrelations(); @@ -212,6 +257,7 @@ export const asyncSearchServiceProvider = ( return { error, + log, isRunning, loaded: Math.round(progress.getOverallProgress() * 100), overallHistogram, diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/get_query_with_params.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/get_query_with_params.test.ts index 12e897ab3eec9..016355b3a6415 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/get_query_with_params.test.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/get_query_with_params.test.ts @@ -10,10 +10,23 @@ import { getQueryWithParams } from './get_query_with_params'; describe('correlations', () => { describe('getQueryWithParams', () => { it('returns the most basic query filtering on processor.event=transaction', () => { - const query = getQueryWithParams({ params: { index: 'apm-*' } }); + const query = getQueryWithParams({ + params: { index: 'apm-*', start: '2020', end: '2021' }, + }); expect(query).toEqual({ bool: { - filter: [{ term: { 'processor.event': 'transaction' } }], + filter: [ + { term: { 'processor.event': 'transaction' } }, + { + range: { + '@timestamp': { + format: 'epoch_millis', + gte: 1577836800000, + lte: 1609459200000, + }, + }, + }, + ], }, }); }); @@ -24,8 +37,8 @@ describe('correlations', () => { index: 'apm-*', serviceName: 'actualServiceName', transactionName: 'actualTransactionName', - start: '01-01-2021', - end: '31-01-2021', + start: '2020', + end: '2021', environment: 'dev', percentileThresholdValue: 75, }, @@ -33,22 +46,17 @@ describe('correlations', () => { expect(query).toEqual({ bool: { filter: [ - { term: { 'processor.event': 'transaction' } }, - { - term: { - 'service.name': 'actualServiceName', - }, - }, { term: { - 'transaction.name': 'actualTransactionName', + 'processor.event': 'transaction', }, }, { range: { '@timestamp': { - gte: '01-01-2021', - lte: '31-01-2021', + format: 'epoch_millis', + gte: 1577836800000, + lte: 1609459200000, }, }, }, @@ -57,6 +65,16 @@ describe('correlations', () => { 'service.environment': 'dev', }, }, + { + term: { + 'service.name': 'actualServiceName', + }, + }, + { + term: { + 'transaction.name': 'actualTransactionName', + }, + }, { range: { 'transaction.duration.us': { @@ -71,7 +89,7 @@ describe('correlations', () => { it('returns a query considering a custom field/value pair', () => { const query = getQueryWithParams({ - params: { index: 'apm-*' }, + params: { index: 'apm-*', start: '2020', end: '2021' }, fieldName: 'actualFieldName', fieldValue: 'actualFieldValue', }); @@ -79,6 +97,15 @@ describe('correlations', () => { bool: { filter: [ { term: { 'processor.event': 'transaction' } }, + { + range: { + '@timestamp': { + format: 'epoch_millis', + gte: 1577836800000, + lte: 1609459200000, + }, + }, + }, { term: { actualFieldName: 'actualFieldValue', diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/get_query_with_params.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/get_query_with_params.ts index 08ba4b23fec35..5d4af3e80f8be 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/get_query_with_params.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/get_query_with_params.ts @@ -6,15 +6,15 @@ */ import type { estypes } from '@elastic/elasticsearch'; -import { - PROCESSOR_EVENT, - SERVICE_NAME, - TRANSACTION_DURATION, - TRANSACTION_NAME, -} from '../../../../common/elasticsearch_fieldnames'; +import { getOrElse } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import * as t from 'io-ts'; +import { failure } from 'io-ts/lib/PathReporter'; +import { TRANSACTION_DURATION } from '../../../../common/elasticsearch_fieldnames'; import type { SearchServiceParams } from '../../../../common/search_strategies/correlations/types'; -import { environmentQuery as getEnvironmentQuery } from '../../../utils/queries'; -import { ProcessorEvent } from '../../../../common/processor_event'; +import { rangeRt } from '../../../routes/default_api_types'; +import { getCorrelationsFilters } from '../../correlations/get_filters'; +import { Setup, SetupTimeRange } from '../../helpers/setup_request'; const getPercentileThresholdValueQuery = ( percentileThresholdValue: number | undefined @@ -39,26 +39,6 @@ export const getTermsQuery = ( return fieldName && fieldValue ? [{ term: { [fieldName]: fieldValue } }] : []; }; -const getRangeQuery = ( - start?: string, - end?: string -): estypes.QueryDslQueryContainer[] => { - if (start === undefined && end === undefined) { - return []; - } - - return [ - { - range: { - '@timestamp': { - ...(start !== undefined ? { gte: start } : {}), - ...(end !== undefined ? { lte: end } : {}), - }, - }, - }, - ]; -}; - interface QueryParams { params: SearchServiceParams; fieldName?: string; @@ -71,21 +51,37 @@ export const getQueryWithParams = ({ }: QueryParams) => { const { environment, + kuery, serviceName, start, end, percentileThresholdValue, + transactionType, transactionName, } = params; + + // converts string based start/end to epochmillis + const setup = pipe( + rangeRt.decode({ start, end }), + getOrElse((errors) => { + throw new Error(failure(errors).join('\n')); + }) + ) as Setup & SetupTimeRange; + + const filters = getCorrelationsFilters({ + setup, + environment, + kuery, + serviceName, + transactionType, + transactionName, + }); + return { bool: { filter: [ - ...getTermsQuery(PROCESSOR_EVENT, ProcessorEvent.transaction), - ...getTermsQuery(SERVICE_NAME, serviceName), - ...getTermsQuery(TRANSACTION_NAME, transactionName), + ...filters, ...getTermsQuery(fieldName, fieldValue), - ...getRangeQuery(start, end), - ...getEnvironmentQuery(environment), ...getPercentileThresholdValueQuery(percentileThresholdValue), ] as estypes.QueryDslQueryContainer[], }, diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_correlation.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_correlation.test.ts index 24741ebaa2dae..678328dce1a19 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_correlation.test.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_correlation.test.ts @@ -15,7 +15,7 @@ import { BucketCorrelation, } from './query_correlation'; -const params = { index: 'apm-*' }; +const params = { index: 'apm-*', start: '2020', end: '2021' }; const expectations = [1, 3, 5]; const ranges = [{ to: 1 }, { from: 1, to: 3 }, { from: 3, to: 5 }, { from: 5 }]; const fractions = [1, 2, 4, 5]; diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_field_candidates.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_field_candidates.test.ts index 89bdd4280d324..8929b31b3ecb1 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_field_candidates.test.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_field_candidates.test.ts @@ -16,7 +16,7 @@ import { shouldBeExcluded, } from './query_field_candidates'; -const params = { index: 'apm-*' }; +const params = { index: 'apm-*', start: '2020', end: '2021' }; describe('query_field_candidates', () => { describe('shouldBeExcluded', () => { @@ -61,6 +61,15 @@ describe('query_field_candidates', () => { 'processor.event': 'transaction', }, }, + { + range: { + '@timestamp': { + format: 'epoch_millis', + gte: 1577836800000, + lte: 1609459200000, + }, + }, + }, ], }, }, diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_field_value_pairs.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_field_value_pairs.test.ts index ea5a1f55bc924..7ffbc5208e41e 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_field_value_pairs.test.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_field_value_pairs.test.ts @@ -16,7 +16,7 @@ import { getTermsAggRequest, } from './query_field_value_pairs'; -const params = { index: 'apm-*' }; +const params = { index: 'apm-*', start: '2020', end: '2021' }; describe('query_field_value_pairs', () => { describe('getTermsAggRequest', () => { diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_fractions.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_fractions.test.ts index 6052841d277c3..3e7d4a52e4de2 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_fractions.test.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_fractions.test.ts @@ -14,7 +14,7 @@ import { getTransactionDurationRangesRequest, } from './query_fractions'; -const params = { index: 'apm-*' }; +const params = { index: 'apm-*', start: '2020', end: '2021' }; const ranges = [{ to: 1 }, { from: 1, to: 3 }, { from: 3, to: 5 }, { from: 5 }]; describe('query_fractions', () => { diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram.test.ts index 2be9446352260..ace9177947960 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram.test.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram.test.ts @@ -14,7 +14,7 @@ import { getTransactionDurationHistogramRequest, } from './query_histogram'; -const params = { index: 'apm-*' }; +const params = { index: 'apm-*', start: '2020', end: '2021' }; const interval = 100; describe('query_histogram', () => { @@ -40,6 +40,15 @@ describe('query_histogram', () => { 'processor.event': 'transaction', }, }, + { + range: { + '@timestamp': { + format: 'epoch_millis', + gte: 1577836800000, + lte: 1609459200000, + }, + }, + }, ], }, }, diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_interval.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_interval.test.ts index 9ed529ccabddb..ebd78f1248510 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_interval.test.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_interval.test.ts @@ -14,7 +14,7 @@ import { getHistogramIntervalRequest, } from './query_histogram_interval'; -const params = { index: 'apm-*' }; +const params = { index: 'apm-*', start: '2020', end: '2021' }; describe('query_histogram_interval', () => { describe('getHistogramIntervalRequest', () => { @@ -43,6 +43,15 @@ describe('query_histogram_interval', () => { 'processor.event': 'transaction', }, }, + { + range: { + '@timestamp': { + format: 'epoch_millis', + gte: 1577836800000, + lte: 1609459200000, + }, + }, + }, ], }, }, diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_rangesteps.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_range_steps.test.ts similarity index 77% rename from x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_rangesteps.test.ts rename to x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_range_steps.test.ts index bb366ea29fed4..76aab1cd979c9 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_rangesteps.test.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_range_steps.test.ts @@ -10,13 +10,13 @@ import type { estypes } from '@elastic/elasticsearch'; import type { ElasticsearchClient } from 'src/core/server'; import { - fetchTransactionDurationHistogramRangesteps, + fetchTransactionDurationHistogramRangeSteps, getHistogramIntervalRequest, -} from './query_histogram_rangesteps'; +} from './query_histogram_range_steps'; -const params = { index: 'apm-*' }; +const params = { index: 'apm-*', start: '2020', end: '2021' }; -describe('query_histogram_rangesteps', () => { +describe('query_histogram_range_steps', () => { describe('getHistogramIntervalRequest', () => { it('returns the request body for the histogram interval request', () => { const req = getHistogramIntervalRequest(params); @@ -43,6 +43,15 @@ describe('query_histogram_rangesteps', () => { 'processor.event': 'transaction', }, }, + { + range: { + '@timestamp': { + format: 'epoch_millis', + gte: 1577836800000, + lte: 1609459200000, + }, + }, + }, ], }, }, @@ -53,13 +62,14 @@ describe('query_histogram_rangesteps', () => { }); }); - describe('fetchTransactionDurationHistogramRangesteps', () => { + describe('fetchTransactionDurationHistogramRangeSteps', () => { it('fetches the range steps for the log histogram', async () => { const esClientSearchMock = jest.fn((req: estypes.SearchRequest): { body: estypes.SearchResponse; } => { return { body: ({ + hits: { total: { value: 10 } }, aggregations: { transaction_duration_max: { value: 10000, @@ -76,7 +86,7 @@ describe('query_histogram_rangesteps', () => { search: esClientSearchMock, } as unknown) as ElasticsearchClient; - const resp = await fetchTransactionDurationHistogramRangesteps( + const resp = await fetchTransactionDurationHistogramRangeSteps( esClientMock, params ); diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_rangesteps.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_range_steps.ts similarity index 83% rename from x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_rangesteps.ts rename to x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_range_steps.ts index e537165ca53f3..6ee5dd6bcdf83 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_rangesteps.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_range_steps.ts @@ -23,6 +23,14 @@ import type { SearchServiceParams } from '../../../../common/search_strategies/c import { getQueryWithParams } from './get_query_with_params'; +const getHistogramRangeSteps = (min: number, max: number, steps: number) => { + // A d3 based scale function as a helper to get equally distributed bins on a log scale. + const logFn = scaleLog().domain([min, max]).range([1, steps]); + return [...Array(steps).keys()] + .map(logFn.invert) + .map((d) => (isNaN(d) ? 0 : d)); +}; + export const getHistogramIntervalRequest = ( params: SearchServiceParams ): estypes.SearchRequest => ({ @@ -37,19 +45,24 @@ export const getHistogramIntervalRequest = ( }, }); -export const fetchTransactionDurationHistogramRangesteps = async ( +export const fetchTransactionDurationHistogramRangeSteps = async ( esClient: ElasticsearchClient, params: SearchServiceParams ): Promise => { + const steps = 100; + const resp = await esClient.search(getHistogramIntervalRequest(params)); + if ((resp.body.hits.total as estypes.SearchTotalHits).value === 0) { + return getHistogramRangeSteps(0, 1, 100); + } + if (resp.body.aggregations === undefined) { throw new Error( - 'fetchTransactionDurationHistogramInterval failed, did not return aggregations.' + 'fetchTransactionDurationHistogramRangeSteps failed, did not return aggregations.' ); } - const steps = 100; const min = (resp.body.aggregations .transaction_duration_min as estypes.AggregationsValueAggregate).value; const max = @@ -57,9 +70,5 @@ export const fetchTransactionDurationHistogramRangesteps = async ( .transaction_duration_max as estypes.AggregationsValueAggregate).value * 2; - // A d3 based scale function as a helper to get equally distributed bins on a log scale. - const logFn = scaleLog().domain([min, max]).range([1, steps]); - return [...Array(steps).keys()] - .map(logFn.invert) - .map((d) => (isNaN(d) ? 0 : d)); + return getHistogramRangeSteps(min, max, steps); }; diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_percentiles.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_percentiles.test.ts index 0c319aee0fb2b..f0d01a4849f9f 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_percentiles.test.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_percentiles.test.ts @@ -14,7 +14,7 @@ import { getTransactionDurationPercentilesRequest, } from './query_percentiles'; -const params = { index: 'apm-*' }; +const params = { index: 'apm-*', start: '2020', end: '2021' }; describe('query_percentiles', () => { describe('getTransactionDurationPercentilesRequest', () => { @@ -41,10 +41,20 @@ describe('query_percentiles', () => { 'processor.event': 'transaction', }, }, + { + range: { + '@timestamp': { + format: 'epoch_millis', + gte: 1577836800000, + lte: 1609459200000, + }, + }, + }, ], }, }, size: 0, + track_total_hits: true, }, index: params.index, }); @@ -53,6 +63,7 @@ describe('query_percentiles', () => { describe('fetchTransactionDurationPercentiles', () => { it('fetches the percentiles', async () => { + const totalDocs = 10; const percentilesValues = { '1.0': 5.0, '5.0': 25.0, @@ -68,6 +79,7 @@ describe('query_percentiles', () => { } => { return { body: ({ + hits: { total: { value: totalDocs } }, aggregations: { transaction_duration_percentiles: { values: percentilesValues, @@ -86,7 +98,7 @@ describe('query_percentiles', () => { params ); - expect(resp).toEqual(percentilesValues); + expect(resp).toEqual({ percentiles: percentilesValues, totalDocs }); expect(esClientSearchMock).toHaveBeenCalledTimes(1); }); }); diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_percentiles.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_percentiles.ts index 18dcefb59a11a..c80f5d836c0ef 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_percentiles.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_percentiles.ts @@ -38,6 +38,7 @@ export const getTransactionDurationPercentilesRequest = ( return { index: params.index, body: { + track_total_hits: true, query, size: 0, aggs: { @@ -61,7 +62,7 @@ export const fetchTransactionDurationPercentiles = async ( percents?: number[], fieldName?: string, fieldValue?: string -): Promise> => { +): Promise<{ totalDocs: number; percentiles: Record }> => { const resp = await esClient.search( getTransactionDurationPercentilesRequest( params, @@ -71,14 +72,22 @@ export const fetchTransactionDurationPercentiles = async ( ) ); + // return early with no results if the search didn't return any documents + if ((resp.body.hits.total as estypes.SearchTotalHits).value === 0) { + return { totalDocs: 0, percentiles: {} }; + } + if (resp.body.aggregations === undefined) { throw new Error( 'fetchTransactionDurationPercentiles failed, did not return aggregations.' ); } - return ( - (resp.body.aggregations - .transaction_duration_percentiles as estypes.AggregationsTDigestPercentilesAggregate) - .values ?? {} - ); + + return { + totalDocs: (resp.body.hits.total as estypes.SearchTotalHits).value, + percentiles: + (resp.body.aggregations + .transaction_duration_percentiles as estypes.AggregationsTDigestPercentilesAggregate) + .values ?? {}, + }; }; diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_ranges.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_ranges.test.ts index 9451928e47ded..7d18efc360563 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_ranges.test.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_ranges.test.ts @@ -14,7 +14,7 @@ import { getTransactionDurationRangesRequest, } from './query_ranges'; -const params = { index: 'apm-*' }; +const params = { index: 'apm-*', start: '2020', end: '2021' }; const rangeSteps = [1, 3, 5]; describe('query_ranges', () => { @@ -59,6 +59,15 @@ describe('query_ranges', () => { 'processor.event': 'transaction', }, }, + { + range: { + '@timestamp': { + format: 'epoch_millis', + gte: 1577836800000, + lte: 1609459200000, + }, + }, + }, ], }, }, diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/search_strategy.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/search_strategy.test.ts index 6d4bfcdde9994..09775cb2eb034 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/search_strategy.test.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/search_strategy.test.ts @@ -122,6 +122,8 @@ describe('APM Correlations search strategy', () => { } as unknown) as SearchStrategyDependencies; params = { index: 'apm-*', + start: '2020', + end: '2021', }; }); @@ -154,10 +156,22 @@ describe('APM Correlations search strategy', () => { }, query: { bool: { - filter: [{ term: { 'processor.event': 'transaction' } }], + filter: [ + { term: { 'processor.event': 'transaction' } }, + { + range: { + '@timestamp': { + format: 'epoch_millis', + gte: 1577836800000, + lte: 1609459200000, + }, + }, + }, + ], }, }, size: 0, + track_total_hits: true, }) ); }); @@ -167,11 +181,17 @@ describe('APM Correlations search strategy', () => { it('retrieves the current request', async () => { const searchStrategy = await apmCorrelationsSearchStrategyProvider(); const response = await searchStrategy - .search({ id: 'my-search-id', params }, {}, mockDeps) + .search({ params }, {}, mockDeps) .toPromise(); - expect(response).toEqual( - expect.objectContaining({ id: 'my-search-id' }) + const searchStrategyId = response.id; + + const response2 = await searchStrategy + .search({ id: searchStrategyId, params }, {}, mockDeps) + .toPromise(); + + expect(response2).toEqual( + expect.objectContaining({ id: searchStrategyId }) ); }); }); @@ -226,7 +246,7 @@ describe('APM Correlations search strategy', () => { expect(response2.id).toEqual(response1.id); expect(response2).toEqual( - expect.objectContaining({ loaded: 10, isRunning: false }) + expect.objectContaining({ loaded: 100, isRunning: false }) ); }); }); diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/search_strategy.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/search_strategy.ts index d6b4e0e7094b3..8f2e6913c0d06 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/search_strategy.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/search_strategy.ts @@ -41,14 +41,40 @@ export const apmCorrelationsSearchStrategyProvider = (): ISearchStrategy< throw new Error('Invalid request parameters.'); } - const id = request.id ?? uuid(); + // The function to fetch the current state of the async search service. + // This will be either an existing service for a follow up fetch or a new one for new requests. + let getAsyncSearchServiceState: ReturnType< + typeof asyncSearchServiceProvider + >; + + // If the request includes an ID, we require that the async search service already exists + // otherwise we throw an error. The client should never poll a service that's been cancelled or finished. + // This also avoids instantiating async search services when the service gets called with random IDs. + if (typeof request.id === 'string') { + const existingGetAsyncSearchServiceState = asyncSearchServiceMap.get( + request.id + ); - const getAsyncSearchServiceState = - asyncSearchServiceMap.get(id) ?? - asyncSearchServiceProvider(deps.esClient.asCurrentUser, request.params); + if (typeof existingGetAsyncSearchServiceState === 'undefined') { + throw new Error( + `AsyncSearchService with ID '${request.id}' does not exist.` + ); + } + + getAsyncSearchServiceState = existingGetAsyncSearchServiceState; + } else { + getAsyncSearchServiceState = asyncSearchServiceProvider( + deps.esClient.asCurrentUser, + request.params + ); + } + + // Reuse the request's id or create a new one. + const id = request.id ?? uuid(); const { error, + log, isRunning, loaded, started, @@ -76,6 +102,7 @@ export const apmCorrelationsSearchStrategyProvider = (): ISearchStrategy< isRunning, isPartial: isRunning, rawResponse: { + log, took, values, percentileThresholdValue, diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/utils/aggregation_utils.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/utils/aggregation_utils.test.ts index 63de0a59d4894..4313ad58ecbc0 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/utils/aggregation_utils.test.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/utils/aggregation_utils.test.ts @@ -14,6 +14,7 @@ describe('aggregation utils', () => { expect(expectations).toEqual([0, 0.5, 1]); expect(ranges).toEqual([{ to: 0 }, { from: 0, to: 1 }, { from: 1 }]); }); + it('returns expectations and ranges based on given percentiles #2', async () => { const { expectations, ranges } = computeExpectationsAndRanges([1, 3, 5]); expect(expectations).toEqual([1, 2, 4, 5]); @@ -24,6 +25,7 @@ describe('aggregation utils', () => { { from: 5 }, ]); }); + it('returns expectations and ranges with adjusted fractions', async () => { const { expectations, ranges } = computeExpectationsAndRanges([ 1, @@ -45,5 +47,97 @@ describe('aggregation utils', () => { { from: 5 }, ]); }); + + // TODO identify these results derived from the array of percentiles are usable with the ES correlation aggregation + it('returns expectation and ranges adjusted when percentiles have equal values', async () => { + const { expectations, ranges } = computeExpectationsAndRanges([ + 5000, + 5000, + 3090428, + 3090428, + 3090428, + 3618812, + 3618812, + 3618812, + 3618812, + 3696636, + 3696636, + 3696636, + 3696636, + 3696636, + 3696636, + ]); + expect(expectations).toEqual([ + 5000, + 1856256.7999999998, + 3392361.714285714, + 3665506.4, + 3696636, + ]); + expect(ranges).toEqual([ + { + to: 5000, + }, + { + from: 5000, + to: 5000, + }, + { + from: 5000, + to: 3090428, + }, + { + from: 3090428, + to: 3090428, + }, + { + from: 3090428, + to: 3090428, + }, + { + from: 3090428, + to: 3618812, + }, + { + from: 3618812, + to: 3618812, + }, + { + from: 3618812, + to: 3618812, + }, + { + from: 3618812, + to: 3618812, + }, + { + from: 3618812, + to: 3696636, + }, + { + from: 3696636, + to: 3696636, + }, + { + from: 3696636, + to: 3696636, + }, + { + from: 3696636, + to: 3696636, + }, + { + from: 3696636, + to: 3696636, + }, + { + from: 3696636, + to: 3696636, + }, + { + from: 3696636, + }, + ]); + }); }); }); 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 6ecfe425dc8c5..8a934a102556e 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 @@ -5,7 +5,7 @@ * 2.0. */ -import { rangeQuery } from '../../../server/utils/queries'; +import { rangeQuery } from '../../../../observability/server'; import { ProcessorEvent } from '../../../common/processor_event'; import { TRACE_ID } from '../../../common/elasticsearch_fieldnames'; import { diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts index 7ac56bcd9192d..7ce4af41f4fec 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts @@ -18,7 +18,7 @@ import { TRANSACTION_PAGE_LOAD, TRANSACTION_REQUEST, } from '../../../common/transaction_types'; -import { rangeQuery } from '../../../server/utils/queries'; +import { rangeQuery } from '../../../../observability/server'; import { withApmSpan } from '../../utils/with_apm_span'; import { getMlJobsWithAPMGroup } from '../anomaly_detection/get_ml_jobs_with_apm_group'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts index 6d50023d3fd0e..5fc022508d0a8 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts @@ -15,7 +15,7 @@ import { } from '../../../common/elasticsearch_fieldnames'; import { getServicesProjection } from '../../projections/services'; import { mergeProjection } from '../../projections/util/merge_projection'; -import { environmentQuery } from '../../../server/utils/queries'; +import { environmentQuery } from '../../../common/utils/environment_query'; import { withApmSpan } from '../../utils/with_apm_span'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts index 2e0ac303e5157..f9af5d227a867 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts @@ -19,7 +19,8 @@ import { TRANSACTION_PAGE_LOAD, TRANSACTION_REQUEST, } from '../../../common/transaction_types'; -import { environmentQuery, rangeQuery } from '../../../server/utils/queries'; +import { rangeQuery } from '../../../../observability/server'; +import { environmentQuery } from '../../../common/utils/environment_query'; import { withApmSpan } from '../../utils/with_apm_span'; import { getDocumentTypeFilterForAggregatedTransactions, diff --git a/x-pack/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts b/x-pack/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts index 26d7d2d1ee316..c97bfc1cfacc8 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts @@ -17,7 +17,8 @@ import { } from '../../../common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../common/processor_event'; import { SERVICE_MAP_TIMEOUT_ERROR } from '../../../common/service_map'; -import { environmentQuery, rangeQuery } from '../../../server/utils/queries'; +import { rangeQuery } from '../../../../observability/server'; +import { environmentQuery } from '../../../common/utils/environment_query'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; const MAX_TRACES_TO_INSPECT = 1000; diff --git a/x-pack/plugins/apm/server/lib/services/annotations/get_derived_service_annotations.ts b/x-pack/plugins/apm/server/lib/services/annotations/get_derived_service_annotations.ts index 08587217980fb..7a36817dfc458 100644 --- a/x-pack/plugins/apm/server/lib/services/annotations/get_derived_service_annotations.ts +++ b/x-pack/plugins/apm/server/lib/services/annotations/get_derived_service_annotations.ts @@ -12,7 +12,8 @@ import { SERVICE_NAME, SERVICE_VERSION, } from '../../../../common/elasticsearch_fieldnames'; -import { environmentQuery, rangeQuery } from '../../../../server/utils/queries'; +import { rangeQuery } from '../../../../../observability/server'; +import { environmentQuery } from '../../../../common/utils/environment_query'; import { getDocumentTypeFilterForAggregatedTransactions, getProcessorEventForAggregatedTransactions, diff --git a/x-pack/plugins/apm/server/lib/services/annotations/get_stored_annotations.ts b/x-pack/plugins/apm/server/lib/services/annotations/get_stored_annotations.ts index 56b7aa1f465b0..1e8b528142936 100644 --- a/x-pack/plugins/apm/server/lib/services/annotations/get_stored_annotations.ts +++ b/x-pack/plugins/apm/server/lib/services/annotations/get_stored_annotations.ts @@ -7,7 +7,8 @@ import { ResponseError } from '@elastic/elasticsearch/lib/errors'; import { ElasticsearchClient, Logger } from 'kibana/server'; -import { environmentQuery, rangeQuery } from '../../../../server/utils/queries'; +import { rangeQuery } from '../../../../../observability/server'; +import { environmentQuery } from '../../../../common/utils/environment_query'; import { unwrapEsResponse, WrappedElasticsearchClientError, diff --git a/x-pack/plugins/apm/server/lib/services/get_service_agent_name.ts b/x-pack/plugins/apm/server/lib/services/get_service_agent_name.ts index 82147d7c94236..49489f2b33888 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_agent_name.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_agent_name.ts @@ -10,7 +10,7 @@ import { AGENT_NAME, SERVICE_NAME, } from '../../../common/elasticsearch_fieldnames'; -import { rangeQuery } from '../../../server/utils/queries'; +import { rangeQuery } from '../../../../observability/server'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { getProcessorEventForAggregatedTransactions } from '../helpers/aggregated_transactions'; diff --git a/x-pack/plugins/apm/server/lib/services/get_service_alerts.ts b/x-pack/plugins/apm/server/lib/services/get_service_alerts.ts index 2141570f521c0..a9fe55456ad4e 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_alerts.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_alerts.ts @@ -11,7 +11,8 @@ import { SERVICE_NAME, TRANSACTION_TYPE, } from '../../../common/elasticsearch_fieldnames'; -import { environmentQuery, rangeQuery } from '../../utils/queries'; +import { rangeQuery } from '../../../../observability/server'; +import { environmentQuery } from '../../../common/utils/environment_query'; export async function getServiceAlerts({ ruleDataClient, diff --git a/x-pack/plugins/apm/server/lib/services/get_service_dependencies/get_destination_map.ts b/x-pack/plugins/apm/server/lib/services/get_service_dependencies/get_destination_map.ts index 4993484f5b240..8382ff820805e 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_dependencies/get_destination_map.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_dependencies/get_destination_map.ts @@ -21,7 +21,8 @@ import { SPAN_TYPE, } from '../../../../common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../../common/processor_event'; -import { environmentQuery, rangeQuery } from '../../../../server/utils/queries'; +import { rangeQuery } from '../../../../../observability/server'; +import { environmentQuery } from '../../../../common/utils/environment_query'; import { joinByKey } from '../../../../common/utils/join_by_key'; import { Setup, SetupTimeRange } from '../../helpers/setup_request'; import { withApmSpan } from '../../../utils/with_apm_span'; diff --git a/x-pack/plugins/apm/server/lib/services/get_service_dependencies/get_metrics.ts b/x-pack/plugins/apm/server/lib/services/get_service_dependencies/get_metrics.ts index 1d815dd7180e3..52d3b41960a50 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_dependencies/get_metrics.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_dependencies/get_metrics.ts @@ -14,7 +14,8 @@ import { SPAN_DESTINATION_SERVICE_RESPONSE_TIME_SUM, } from '../../../../common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../../common/processor_event'; -import { environmentQuery, rangeQuery } from '../../../../server/utils/queries'; +import { environmentQuery } from '../../../../common/utils/environment_query'; +import { rangeQuery } from '../../../../../observability/server'; import { getBucketSize } from '../../helpers/get_bucket_size'; import { EventOutcome } from '../../../../common/event_outcome'; import { Setup, SetupTimeRange } from '../../helpers/setup_request'; diff --git a/x-pack/plugins/apm/server/lib/services/get_service_error_groups/get_service_error_group_detailed_statistics.ts b/x-pack/plugins/apm/server/lib/services/get_service_error_groups/get_service_error_group_detailed_statistics.ts index bd69bfc53db71..1e0c8e6f60441 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_error_groups/get_service_error_group_detailed_statistics.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_error_groups/get_service_error_group_detailed_statistics.ts @@ -13,11 +13,8 @@ import { TRANSACTION_TYPE, } from '../../../../common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../../common/processor_event'; -import { - environmentQuery, - rangeQuery, - kqlQuery, -} from '../../../../server/utils/queries'; +import { rangeQuery, kqlQuery } from '../../../../../observability/server'; +import { environmentQuery } from '../../../../common/utils/environment_query'; import { getBucketSize } from '../../helpers/get_bucket_size'; import { Setup, SetupTimeRange } from '../../helpers/setup_request'; diff --git a/x-pack/plugins/apm/server/lib/services/get_service_error_groups/get_service_error_group_main_statistics.ts b/x-pack/plugins/apm/server/lib/services/get_service_error_groups/get_service_error_group_main_statistics.ts index 8168c0d5549aa..9922cbce83dfd 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_error_groups/get_service_error_group_main_statistics.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_error_groups/get_service_error_group_main_statistics.ts @@ -14,11 +14,8 @@ import { } from '../../../../common/elasticsearch_fieldnames'; import { NOT_AVAILABLE_LABEL } from '../../../../common/i18n'; import { ProcessorEvent } from '../../../../common/processor_event'; -import { - environmentQuery, - rangeQuery, - kqlQuery, -} from '../../../../server/utils/queries'; +import { rangeQuery, kqlQuery } from '../../../../../observability/server'; +import { environmentQuery } from '../../../../common/utils/environment_query'; import { getErrorName } from '../../helpers/get_error_name'; import { Setup, SetupTimeRange } from '../../helpers/setup_request'; diff --git a/x-pack/plugins/apm/server/lib/services/get_service_error_groups/index.ts b/x-pack/plugins/apm/server/lib/services/get_service_error_groups/index.ts index b720c56464c30..0e9f8282301bf 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_error_groups/index.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_error_groups/index.ts @@ -9,11 +9,8 @@ import { ValuesType } from 'utility-types'; import { orderBy } from 'lodash'; import { NOT_AVAILABLE_LABEL } from '../../../../common/i18n'; import { PromiseReturnType } from '../../../../../observability/typings/common'; -import { - environmentQuery, - rangeQuery, - kqlQuery, -} from '../../../../server/utils/queries'; +import { rangeQuery, kqlQuery } from '../../../../../observability/server'; +import { environmentQuery } from '../../../../common/utils/environment_query'; import { ProcessorEvent } from '../../../../common/processor_event'; import { ERROR_EXC_MESSAGE, diff --git a/x-pack/plugins/apm/server/lib/services/get_service_instance_metadata_details.ts b/x-pack/plugins/apm/server/lib/services/get_service_instance_metadata_details.ts index bdf9530a9c0c7..44dfc2f600efc 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_instance_metadata_details.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_instance_metadata_details.ts @@ -10,7 +10,8 @@ import { SERVICE_NODE_NAME, TRANSACTION_TYPE, } from '../../../common/elasticsearch_fieldnames'; -import { environmentQuery, kqlQuery, rangeQuery } from '../../utils/queries'; +import { kqlQuery, rangeQuery } from '../../../../observability/server'; +import { environmentQuery } from '../../../common/utils/environment_query'; import { getProcessorEventForAggregatedTransactions } from '../helpers/aggregated_transactions'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; diff --git a/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instances_system_metric_statistics.ts b/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instances_system_metric_statistics.ts index b817d4fb654ce..48209d98e86ce 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instances_system_metric_statistics.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instances_system_metric_statistics.ts @@ -17,7 +17,8 @@ import { import { ProcessorEvent } from '../../../../common/processor_event'; import { SERVICE_NODE_NAME_MISSING } from '../../../../common/service_nodes'; import { Coordinate } from '../../../../typings/timeseries'; -import { environmentQuery, kqlQuery, rangeQuery } from '../../../utils/queries'; +import { kqlQuery, rangeQuery } from '../../../../../observability/server'; +import { environmentQuery } from '../../../../common/utils/environment_query'; import { getBucketSize } from '../../helpers/get_bucket_size'; import { Setup } from '../../helpers/setup_request'; import { diff --git a/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instances_transaction_statistics.ts b/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instances_transaction_statistics.ts index 6110ad3459911..d0f58ee8be31f 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instances_transaction_statistics.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_instances/get_service_instances_transaction_statistics.ts @@ -14,7 +14,8 @@ import { EventOutcome } from '../../../../common/event_outcome'; import { LatencyAggregationType } from '../../../../common/latency_aggregation_types'; import { SERVICE_NODE_NAME_MISSING } from '../../../../common/service_nodes'; import { Coordinate } from '../../../../typings/timeseries'; -import { environmentQuery, kqlQuery, rangeQuery } from '../../../utils/queries'; +import { kqlQuery, rangeQuery } from '../../../../../observability/server'; +import { environmentQuery } from '../../../../common/utils/environment_query'; import { getProcessorEventForAggregatedTransactions, getTransactionDurationFieldForAggregatedTransactions, diff --git a/x-pack/plugins/apm/server/lib/services/get_service_metadata_details.ts b/x-pack/plugins/apm/server/lib/services/get_service_metadata_details.ts index 910725b005411..60b0628017efb 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_metadata_details.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_metadata_details.ts @@ -20,7 +20,7 @@ import { SERVICE_VERSION, } from '../../../common/elasticsearch_fieldnames'; import { ContainerType } from '../../../common/service_metadata'; -import { rangeQuery } from '../../../server/utils/queries'; +import { rangeQuery } from '../../../../observability/server'; import { TransactionRaw } from '../../../typings/es_schemas/raw/transaction_raw'; import { getProcessorEventForAggregatedTransactions } from '../helpers/aggregated_transactions'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; diff --git a/x-pack/plugins/apm/server/lib/services/get_service_metadata_icons.ts b/x-pack/plugins/apm/server/lib/services/get_service_metadata_icons.ts index 469c788a6cf17..ce9a9f59d0e20 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_metadata_icons.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_metadata_icons.ts @@ -16,7 +16,7 @@ import { HOST_OS_PLATFORM, } from '../../../common/elasticsearch_fieldnames'; import { ContainerType } from '../../../common/service_metadata'; -import { rangeQuery } from '../../../server/utils/queries'; +import { rangeQuery } from '../../../../observability/server'; import { TransactionRaw } from '../../../typings/es_schemas/raw/transaction_raw'; import { getProcessorEventForAggregatedTransactions } from '../helpers/aggregated_transactions'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; diff --git a/x-pack/plugins/apm/server/lib/services/get_service_transaction_group_detailed_statistics.ts b/x-pack/plugins/apm/server/lib/services/get_service_transaction_group_detailed_statistics.ts index ea33c942cfc3b..6edec60b6f373 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_transaction_group_detailed_statistics.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_transaction_group_detailed_statistics.ts @@ -15,11 +15,8 @@ import { import { EventOutcome } from '../../../common/event_outcome'; import { LatencyAggregationType } from '../../../common/latency_aggregation_types'; import { offsetPreviousPeriodCoordinates } from '../../../common/utils/offset_previous_period_coordinate'; -import { - environmentQuery, - kqlQuery, - rangeQuery, -} from '../../../server/utils/queries'; +import { kqlQuery, rangeQuery } from '../../../../observability/server'; +import { environmentQuery } from '../../../common/utils/environment_query'; import { Coordinate } from '../../../typings/timeseries'; import { getDocumentTypeFilterForAggregatedTransactions, diff --git a/x-pack/plugins/apm/server/lib/services/get_service_transaction_groups.ts b/x-pack/plugins/apm/server/lib/services/get_service_transaction_groups.ts index a4cc27c875d73..223abf972ee24 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_transaction_groups.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_transaction_groups.ts @@ -13,11 +13,8 @@ import { } from '../../../common/elasticsearch_fieldnames'; import { EventOutcome } from '../../../common/event_outcome'; import { LatencyAggregationType } from '../../../common/latency_aggregation_types'; -import { - environmentQuery, - rangeQuery, - kqlQuery, -} from '../../../server/utils/queries'; +import { rangeQuery, kqlQuery } from '../../../../observability/server'; +import { environmentQuery } from '../../../common/utils/environment_query'; import { getDocumentTypeFilterForAggregatedTransactions, getProcessorEventForAggregatedTransactions, diff --git a/x-pack/plugins/apm/server/lib/services/get_service_transaction_types.ts b/x-pack/plugins/apm/server/lib/services/get_service_transaction_types.ts index f38a7fba09d96..8d5a9248abce9 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_transaction_types.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_transaction_types.ts @@ -9,7 +9,7 @@ import { SERVICE_NAME, TRANSACTION_TYPE, } from '../../../common/elasticsearch_fieldnames'; -import { rangeQuery } from '../../../server/utils/queries'; +import { rangeQuery } from '../../../../observability/server'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { getDocumentTypeFilterForAggregatedTransactions, diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_legacy_data_status.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_legacy_data_status.ts index f33bedb6ef4fb..cf8b83464b6d4 100644 --- a/x-pack/plugins/apm/server/lib/services/get_services/get_legacy_data_status.ts +++ b/x-pack/plugins/apm/server/lib/services/get_services/get_legacy_data_status.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { rangeQuery } from '../../../../server/utils/queries'; +import { rangeQuery } from '../../../../../observability/server'; import { ProcessorEvent } from '../../../../common/processor_event'; import { OBSERVER_VERSION_MAJOR } from '../../../../common/elasticsearch_fieldnames'; import { Setup, SetupTimeRange } from '../../helpers/setup_request'; 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 index 7f48c591521e7..c2121dbba97ef 100644 --- 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 @@ -15,11 +15,8 @@ import { TRANSACTION_PAGE_LOAD, TRANSACTION_REQUEST, } from '../../../../common/transaction_types'; -import { - environmentQuery, - kqlQuery, - rangeQuery, -} from '../../../../server/utils/queries'; +import { kqlQuery, rangeQuery } from '../../../../../observability/server'; +import { environmentQuery } from '../../../../common/utils/environment_query'; import { AgentName } from '../../../../typings/es_schemas/ui/fields/agent'; import { getDocumentTypeFilterForAggregatedTransactions, diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_services_from_metric_documents.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_services_from_metric_documents.ts index 4692d1122b16c..a3bab48646eb6 100644 --- a/x-pack/plugins/apm/server/lib/services/get_services/get_services_from_metric_documents.ts +++ b/x-pack/plugins/apm/server/lib/services/get_services/get_services_from_metric_documents.ts @@ -11,7 +11,8 @@ import { SERVICE_ENVIRONMENT, SERVICE_NAME, } from '../../../../common/elasticsearch_fieldnames'; -import { environmentQuery, kqlQuery, rangeQuery } from '../../../utils/queries'; +import { kqlQuery, rangeQuery } from '../../../../../observability/server'; +import { environmentQuery } from '../../../../common/utils/environment_query'; import { ProcessorEvent } from '../../../../common/processor_event'; import { Setup, SetupTimeRange } from '../../helpers/setup_request'; diff --git a/x-pack/plugins/apm/server/lib/services/get_throughput.ts b/x-pack/plugins/apm/server/lib/services/get_throughput.ts index 7eacf47f15b7a..4004d55da79f6 100644 --- a/x-pack/plugins/apm/server/lib/services/get_throughput.ts +++ b/x-pack/plugins/apm/server/lib/services/get_throughput.ts @@ -10,11 +10,8 @@ import { SERVICE_NAME, TRANSACTION_TYPE, } from '../../../common/elasticsearch_fieldnames'; -import { - environmentQuery, - kqlQuery, - rangeQuery, -} from '../../../server/utils/queries'; +import { kqlQuery, rangeQuery } from '../../../../observability/server'; +import { environmentQuery } from '../../../common/utils/environment_query'; import { getDocumentTypeFilterForAggregatedTransactions, getProcessorEventForAggregatedTransactions, diff --git a/x-pack/plugins/apm/server/lib/services/profiling/get_service_profiling_statistics.ts b/x-pack/plugins/apm/server/lib/services/profiling/get_service_profiling_statistics.ts index 4e88c752aa50b..4434cc89d24f2 100644 --- a/x-pack/plugins/apm/server/lib/services/profiling/get_service_profiling_statistics.ts +++ b/x-pack/plugins/apm/server/lib/services/profiling/get_service_profiling_statistics.ts @@ -21,11 +21,8 @@ import { PROFILE_TOP_ID, SERVICE_NAME, } from '../../../../common/elasticsearch_fieldnames'; -import { - rangeQuery, - environmentQuery, - kqlQuery, -} from '../../../../server/utils/queries'; +import { rangeQuery, kqlQuery } from '../../../../../observability/server'; +import { environmentQuery } from '../../../../common/utils/environment_query'; import { APMEventClient } from '../../helpers/create_es_client/create_apm_event_client'; import { Setup, SetupTimeRange } from '../../helpers/setup_request'; import { withApmSpan } from '../../../utils/with_apm_span'; diff --git a/x-pack/plugins/apm/server/lib/services/profiling/get_service_profiling_timeline.ts b/x-pack/plugins/apm/server/lib/services/profiling/get_service_profiling_timeline.ts index af3cd6596a8c1..19de91a5a1055 100644 --- a/x-pack/plugins/apm/server/lib/services/profiling/get_service_profiling_timeline.ts +++ b/x-pack/plugins/apm/server/lib/services/profiling/get_service_profiling_timeline.ts @@ -5,7 +5,6 @@ * 2.0. */ import { mapKeys, mapValues } from 'lodash'; -import { rangeQuery, environmentQuery } from '../../../../server/utils/queries'; import { ProcessorEvent } from '../../../../common/processor_event'; import { PROFILE_ID, @@ -17,7 +16,8 @@ import { } from '../../../../common/profiling'; import { Setup, SetupTimeRange } from '../../helpers/setup_request'; import { getBucketSize } from '../../helpers/get_bucket_size'; -import { kqlQuery } from '../../../utils/queries'; +import { environmentQuery } from '../../../../common/utils/environment_query'; +import { kqlQuery, rangeQuery } from '../../../../../observability/server'; const configMap = mapValues( mapKeys(ProfilingValueType, (val, key) => val), diff --git a/x-pack/plugins/apm/server/lib/traces/get_trace_items.ts b/x-pack/plugins/apm/server/lib/traces/get_trace_items.ts index 68d316ef55df9..026cf9dcceb79 100644 --- a/x-pack/plugins/apm/server/lib/traces/get_trace_items.ts +++ b/x-pack/plugins/apm/server/lib/traces/get_trace_items.ts @@ -16,7 +16,7 @@ import { ERROR_LOG_LEVEL, } from '../../../common/elasticsearch_fieldnames'; import { APMError } from '../../../typings/es_schemas/ui/apm_error'; -import { rangeQuery } from '../../../server/utils/queries'; +import { rangeQuery } from '../../../../observability/server'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { PromiseValueType } from '../../../typings/common'; diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts b/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts index cc3a13ef5c648..b27a54a983734 100644 --- a/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts +++ b/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts @@ -13,11 +13,8 @@ import { } from '../../../common/elasticsearch_fieldnames'; import { EventOutcome } from '../../../common/event_outcome'; import { offsetPreviousPeriodCoordinates } from '../../../common/utils/offset_previous_period_coordinate'; -import { - environmentQuery, - kqlQuery, - rangeQuery, -} from '../../../server/utils/queries'; +import { kqlQuery, rangeQuery } from '../../../../observability/server'; +import { environmentQuery } from '../../../common/utils/environment_query'; import { Coordinate } from '../../../typings/timeseries'; import { getDocumentTypeFilterForAggregatedTransactions, diff --git a/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts index 20534a5fa7cbf..f5fcb3c2917ea 100644 --- a/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts +++ b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts @@ -18,11 +18,8 @@ import { TRANSACTION_BREAKDOWN_COUNT, } from '../../../../common/elasticsearch_fieldnames'; import { Setup, SetupTimeRange } from '../../helpers/setup_request'; -import { - environmentQuery, - rangeQuery, - kqlQuery, -} from '../../../../server/utils/queries'; +import { rangeQuery, kqlQuery } from '../../../../../observability/server'; +import { environmentQuery } from '../../../../common/utils/environment_query'; import { getMetricsDateHistogramParams } from '../../helpers/metrics'; import { MAX_KPIS } from './constants'; import { getVizColorForIndex } from '../../../../common/viz_colors'; diff --git a/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts b/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts index 6259bb75386fb..62c284cd22053 100644 --- a/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts +++ b/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts @@ -17,11 +17,8 @@ import { } from '../../../../../common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../../../common/processor_event'; import { joinByKey } from '../../../../../common/utils/join_by_key'; -import { - environmentQuery, - rangeQuery, - kqlQuery, -} from '../../../../../server/utils/queries'; +import { rangeQuery, kqlQuery } from '../../../../../../observability/server'; +import { environmentQuery } from '../../../../../common/utils/environment_query'; import { getDocumentTypeFilterForAggregatedTransactions, getProcessorEventForAggregatedTransactions, diff --git a/x-pack/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts b/x-pack/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts index f3d4e8f6dd92d..34b790a267c83 100644 --- a/x-pack/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts +++ b/x-pack/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts @@ -15,11 +15,8 @@ import { getProcessorEventForAggregatedTransactions, getTransactionDurationFieldForAggregatedTransactions, } from '../../helpers/aggregated_transactions'; -import { - environmentQuery, - rangeQuery, - kqlQuery, -} from '../../../../server/utils/queries'; +import { rangeQuery, kqlQuery } from '../../../../../observability/server'; +import { environmentQuery } from '../../../../common/utils/environment_query'; export async function getDistributionMax({ environment, diff --git a/x-pack/plugins/apm/server/lib/transactions/get_anomaly_data/fetcher.ts b/x-pack/plugins/apm/server/lib/transactions/get_anomaly_data/fetcher.ts index 558db17939354..95cca0081e5ba 100644 --- a/x-pack/plugins/apm/server/lib/transactions/get_anomaly_data/fetcher.ts +++ b/x-pack/plugins/apm/server/lib/transactions/get_anomaly_data/fetcher.ts @@ -8,7 +8,7 @@ import { QueryDslQueryContainer } from '@elastic/elasticsearch/api/types'; import { ESSearchResponse } from '../../../../../../../src/core/types/elasticsearch'; import { PromiseReturnType } from '../../../../../observability/typings/common'; -import { rangeQuery } from '../../../../server/utils/queries'; +import { rangeQuery } from '../../../../../observability/server'; import { asMutableArray } from '../../../../common/utils/as_mutable_array'; import { withApmSpan } from '../../../utils/with_apm_span'; import { Setup } from '../../helpers/setup_request'; diff --git a/x-pack/plugins/apm/server/lib/transactions/get_latency_charts/index.ts b/x-pack/plugins/apm/server/lib/transactions/get_latency_charts/index.ts index e3f59ca2e4328..183a754ea0809 100644 --- a/x-pack/plugins/apm/server/lib/transactions/get_latency_charts/index.ts +++ b/x-pack/plugins/apm/server/lib/transactions/get_latency_charts/index.ts @@ -14,11 +14,8 @@ import { } from '../../../../common/elasticsearch_fieldnames'; import { LatencyAggregationType } from '../../../../common/latency_aggregation_types'; import { offsetPreviousPeriodCoordinates } from '../../../../common/utils/offset_previous_period_coordinate'; -import { - environmentQuery, - kqlQuery, - rangeQuery, -} from '../../../../server/utils/queries'; +import { kqlQuery, rangeQuery } from '../../../../../observability/server'; +import { environmentQuery } from '../../../../common/utils/environment_query'; import { getDocumentTypeFilterForAggregatedTransactions, getProcessorEventForAggregatedTransactions, diff --git a/x-pack/plugins/apm/server/lib/transactions/get_throughput_charts/index.ts b/x-pack/plugins/apm/server/lib/transactions/get_throughput_charts/index.ts index ff3534159d19b..d5fff20496280 100644 --- a/x-pack/plugins/apm/server/lib/transactions/get_throughput_charts/index.ts +++ b/x-pack/plugins/apm/server/lib/transactions/get_throughput_charts/index.ts @@ -13,11 +13,8 @@ import { TRANSACTION_RESULT, TRANSACTION_TYPE, } from '../../../../common/elasticsearch_fieldnames'; -import { - environmentQuery, - kqlQuery, - rangeQuery, -} from '../../../../server/utils/queries'; +import { kqlQuery, rangeQuery } from '../../../../../observability/server'; +import { environmentQuery } from '../../../../common/utils/environment_query'; import { getDocumentTypeFilterForAggregatedTransactions, getProcessorEventForAggregatedTransactions, diff --git a/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts b/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts index c928b00cefb63..87d205f2bcd11 100644 --- a/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts +++ b/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts @@ -9,7 +9,7 @@ import { TRACE_ID, TRANSACTION_ID, } from '../../../../common/elasticsearch_fieldnames'; -import { rangeQuery } from '../../../../server/utils/queries'; +import { rangeQuery } from '../../../../../observability/server'; import { Setup, SetupTimeRange } from '../../helpers/setup_request'; import { ProcessorEvent } from '../../../../common/processor_event'; import { asMutableArray } from '../../../../common/utils/as_mutable_array'; diff --git a/x-pack/plugins/apm/server/projections/errors.ts b/x-pack/plugins/apm/server/projections/errors.ts index 341c7d13936ba..8f32dd9e5be58 100644 --- a/x-pack/plugins/apm/server/projections/errors.ts +++ b/x-pack/plugins/apm/server/projections/errors.ts @@ -10,11 +10,8 @@ import { SERVICE_NAME, ERROR_GROUP_ID, } from '../../common/elasticsearch_fieldnames'; -import { - environmentQuery, - rangeQuery, - kqlQuery, -} from '../../server/utils/queries'; +import { rangeQuery, kqlQuery } from '../../../observability/server'; +import { environmentQuery } from '../../common/utils/environment_query'; import { ProcessorEvent } from '../../common/processor_event'; export function getErrorGroupsProjection({ diff --git a/x-pack/plugins/apm/server/projections/metrics.ts b/x-pack/plugins/apm/server/projections/metrics.ts index 9a757893337e5..54bc498007cbb 100644 --- a/x-pack/plugins/apm/server/projections/metrics.ts +++ b/x-pack/plugins/apm/server/projections/metrics.ts @@ -11,11 +11,8 @@ import { SERVICE_NAME, SERVICE_NODE_NAME, } from '../../common/elasticsearch_fieldnames'; -import { - environmentQuery, - rangeQuery, - kqlQuery, -} from '../../server/utils/queries'; +import { rangeQuery, kqlQuery } from '../../../observability/server'; +import { environmentQuery } from '../../common/utils/environment_query'; import { SERVICE_NODE_NAME_MISSING } from '../../common/service_nodes'; import { ProcessorEvent } from '../../common/processor_event'; diff --git a/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts b/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts index ae8bb4a5f11af..b8cf92f15c706 100644 --- a/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts +++ b/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts @@ -11,7 +11,7 @@ import { TRANSACTION_TYPE, SERVICE_LANGUAGE_NAME, } from '../../common/elasticsearch_fieldnames'; -import { rangeQuery } from '../../server/utils/queries'; +import { rangeQuery } from '../../../observability/server'; import { ProcessorEvent } from '../../common/processor_event'; import { TRANSACTION_PAGE_LOAD } from '../../common/transaction_types'; import { getEsFilter } from '../lib/rum_client/ui_filters/get_es_filter'; diff --git a/x-pack/plugins/apm/server/projections/services.ts b/x-pack/plugins/apm/server/projections/services.ts index 3509e4fa5b339..52992f16dac85 100644 --- a/x-pack/plugins/apm/server/projections/services.ts +++ b/x-pack/plugins/apm/server/projections/services.ts @@ -7,7 +7,7 @@ import { Setup, SetupTimeRange } from '../../server/lib/helpers/setup_request'; import { SERVICE_NAME } from '../../common/elasticsearch_fieldnames'; -import { rangeQuery, kqlQuery } from '../../server/utils/queries'; +import { rangeQuery, kqlQuery } from '../../../observability/server'; import { ProcessorEvent } from '../../common/processor_event'; import { getProcessorEventForAggregatedTransactions } from '../lib/helpers/aggregated_transactions'; diff --git a/x-pack/plugins/apm/server/projections/transactions.ts b/x-pack/plugins/apm/server/projections/transactions.ts index 3e830403debb0..1efd9679cae9c 100644 --- a/x-pack/plugins/apm/server/projections/transactions.ts +++ b/x-pack/plugins/apm/server/projections/transactions.ts @@ -12,11 +12,8 @@ import { TRANSACTION_TYPE, TRANSACTION_NAME, } from '../../common/elasticsearch_fieldnames'; -import { - environmentQuery, - rangeQuery, - kqlQuery, -} from '../../server/utils/queries'; +import { rangeQuery, kqlQuery } from '../../../observability/server'; +import { environmentQuery } from '../../common/utils/environment_query'; import { getProcessorEventForAggregatedTransactions, getDocumentTypeFilterForAggregatedTransactions, diff --git a/x-pack/plugins/apm/server/routes/fleet.ts b/x-pack/plugins/apm/server/routes/fleet.ts index b760014d6af89..a01c9dd1579b1 100644 --- a/x-pack/plugins/apm/server/routes/fleet.ts +++ b/x-pack/plugins/apm/server/routes/fleet.ts @@ -5,28 +5,27 @@ * 2.0. */ -import { keyBy } from 'lodash'; import Boom from '@hapi/boom'; -import * as t from 'io-ts'; import { i18n } from '@kbn/i18n'; +import * as t from 'io-ts'; +import { keyBy } from 'lodash'; import { - APM_SERVER_SCHEMA_SAVED_OBJECT_TYPE, APM_SERVER_SCHEMA_SAVED_OBJECT_ID, + APM_SERVER_SCHEMA_SAVED_OBJECT_TYPE, } from '../../common/apm_saved_object_constants'; +import { createCloudApmPackgePolicy } from '../lib/fleet/create_cloud_apm_package_policy'; import { getFleetAgents } from '../lib/fleet/get_agents'; import { getApmPackgePolicies } from '../lib/fleet/get_apm_package_policies'; -import { createApmServerRoute } from './create_apm_server_route'; -import { createApmServerRouteRepository } from './create_apm_server_route_repository'; import { - getCloudAgentPolicy, getApmPackagePolicy, + getCloudAgentPolicy, } from '../lib/fleet/get_cloud_apm_package_policy'; -import { createCloudApmPackgePolicy } from '../lib/fleet/create_cloud_apm_package_policy'; import { getUnsupportedApmServerSchema } from '../lib/fleet/get_unsupported_apm_server_schema'; import { isSuperuser } from '../lib/fleet/is_superuser'; import { getInternalSavedObjectsClient } from '../lib/helpers/get_internal_saved_objects_client'; import { setupRequest } from '../lib/helpers/setup_request'; -import { createStaticIndexPattern } from '../lib/index_pattern/create_static_index_pattern'; +import { createApmServerRoute } from './create_apm_server_route'; +import { createApmServerRouteRepository } from './create_apm_server_route_repository'; const hasFleetDataRoute = createApmServerRoute({ endpoint: 'GET /api/apm/fleet/has_data', @@ -156,7 +155,7 @@ const createCloudApmPackagePolicyRoute = createApmServerRoute({ endpoint: 'POST /api/apm/fleet/cloud_apm_package_policy', options: { tags: ['access:apm', 'access:apm_write'] }, handler: async (resources) => { - const { plugins, context, config, request, logger, core } = resources; + const { plugins, context, config, request, logger } = resources; const cloudApmMigrationEnabled = config['xpack.apm.agent.migrations.enabled']; if (!plugins.fleet || !plugins.security) { @@ -174,33 +173,18 @@ const createCloudApmPackagePolicyRoute = createApmServerRoute({ throw Boom.forbidden(CLOUD_SUPERUSER_REQUIRED_MESSAGE); } - const cloudApmAackagePolicy = await createCloudApmPackgePolicy({ + const setup = await setupRequest(resources); + + const cloudApmPackagePolicy = await createCloudApmPackgePolicy({ cloudPluginSetup, fleetPluginStart, savedObjectsClient, esClient, logger, - }); - - const [setup, internalSavedObjectsClient] = await Promise.all([ - setupRequest(resources), - core - .start() - .then(({ savedObjects }) => savedObjects.createInternalRepository()), - ]); - - const spaceId = plugins.spaces?.setup.spacesService.getSpaceId(request); - - // force update the index pattern title with data streams - await createStaticIndexPattern( setup, - config, - internalSavedObjectsClient, - spaceId, - true - ); + }); - return { cloud_apm_package_policy: cloudApmAackagePolicy }; + return { cloudApmPackagePolicy }; }, }); diff --git a/x-pack/plugins/apm/server/routes/index_pattern.ts b/x-pack/plugins/apm/server/routes/index_pattern.ts index aa70cde4f96ae..190baf3bbc270 100644 --- a/x-pack/plugins/apm/server/routes/index_pattern.ts +++ b/x-pack/plugins/apm/server/routes/index_pattern.ts @@ -32,12 +32,12 @@ const staticIndexPatternRoute = createApmServerRoute({ const spaceId = spaces?.setup.spacesService.getSpaceId(request); - const didCreateIndexPattern = await createStaticIndexPattern( + const didCreateIndexPattern = await createStaticIndexPattern({ setup, config, savedObjectsClient, - spaceId - ); + spaceId, + }); return { created: didCreateIndexPattern }; }, diff --git a/x-pack/plugins/apm/server/routes/register_routes/index.ts b/x-pack/plugins/apm/server/routes/register_routes/index.ts index 8e6070de722be..16e77f59f4d02 100644 --- a/x-pack/plugins/apm/server/routes/register_routes/index.ts +++ b/x-pack/plugins/apm/server/routes/register_routes/index.ts @@ -18,9 +18,12 @@ import { routeValidationObject, } from '@kbn/server-route-repository'; import { mergeRt, jsonRt } from '@kbn/io-ts-utils'; -import { UsageCollectionSetup } from '../../../../../../src/plugins/usage_collection/server'; import { pickKeys } from '../../../common/utils/pick_keys'; -import { APMRouteHandlerResources, InspectResponse } from '../typings'; +import { + APMRouteHandlerResources, + InspectResponse, + TelemetryUsageCounter, +} from '../typings'; import type { ApmPluginRequestHandlerContext } from '../typings'; const inspectRt = t.exact( @@ -56,9 +59,7 @@ export function registerRoutes({ repository: ServerRouteRepository; config: APMRouteHandlerResources['config']; ruleDataClient: APMRouteHandlerResources['ruleDataClient']; - telemetryUsageCounter?: ReturnType< - UsageCollectionSetup['createUsageCounter'] - >; + telemetryUsageCounter?: TelemetryUsageCounter; }) { const routes = repository.getRoutes(); @@ -104,6 +105,7 @@ export function registerRoutes({ logger, core, plugins, + telemetryUsageCounter, params: merge( { query: { diff --git a/x-pack/plugins/apm/server/routes/settings/agent_configuration.ts b/x-pack/plugins/apm/server/routes/settings/agent_configuration.ts index 05eec47893793..f50770cb5ded7 100644 --- a/x-pack/plugins/apm/server/routes/settings/agent_configuration.ts +++ b/x-pack/plugins/apm/server/routes/settings/agent_configuration.ts @@ -79,7 +79,7 @@ const deleteAgentConfigurationRoute = createApmServerRoute({ }), handler: async (resources) => { const setup = await setupRequest(resources); - const { params, logger, core } = resources; + const { params, logger, core, telemetryUsageCounter } = resources; const { service } = params.body; @@ -106,6 +106,7 @@ const deleteAgentConfigurationRoute = createApmServerRoute({ core, fleetPluginStart: await resources.plugins.fleet.start(), setup, + telemetryUsageCounter, }); logger.info( `Updated Fleet integration policy for APM to remove the deleted agent configuration.` @@ -128,7 +129,7 @@ const createOrUpdateAgentConfigurationRoute = createApmServerRoute({ ]), handler: async (resources) => { const setup = await setupRequest(resources); - const { params, logger, core } = resources; + const { params, logger, core, telemetryUsageCounter } = resources; const { body, query } = params; // if the config already exists, it is fetched and updated @@ -162,6 +163,7 @@ const createOrUpdateAgentConfigurationRoute = createApmServerRoute({ core, fleetPluginStart: await resources.plugins.fleet.start(), setup, + telemetryUsageCounter, }); logger.info( `Saved latest agent settings to Fleet integration policy for APM.` diff --git a/x-pack/plugins/apm/server/routes/typings.ts b/x-pack/plugins/apm/server/routes/typings.ts index 56a5950c27367..4279cfd84328c 100644 --- a/x-pack/plugins/apm/server/routes/typings.ts +++ b/x-pack/plugins/apm/server/routes/typings.ts @@ -18,6 +18,7 @@ import type { RacApiRequestHandlerContext } from '../../../rule_registry/server' import { LicensingApiRequestHandlerContext } from '../../../licensing/server'; import { APMConfig } from '..'; import { APMPluginDependencies } from '../types'; +import { UsageCollectionSetup } from '../../../../../src/plugins/usage_collection/server'; export interface ApmPluginRequestHandlerContext extends RequestHandlerContext { licensing: LicensingApiRequestHandlerContext; @@ -47,6 +48,10 @@ export interface APMRouteCreateOptions { }; } +export type TelemetryUsageCounter = ReturnType< + UsageCollectionSetup['createUsageCounter'] +>; + export interface APMRouteHandlerResources { request: KibanaRequest; context: ApmPluginRequestHandlerContext; @@ -68,4 +73,5 @@ export interface APMRouteHandlerResources { }; }; ruleDataClient: RuleDataClient; + telemetryUsageCounter?: TelemetryUsageCounter; } diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/core.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/core.ts index 3295332bb6316..906f1646757a7 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/core.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/core.ts @@ -5,8 +5,6 @@ * 2.0. */ -import { debug } from './debug'; -import { error } from './error'; import { image } from './image'; import { markdown } from './markdown'; import { metric } from './metric'; @@ -19,8 +17,6 @@ import { table } from './table'; import { text } from './text'; export const renderFunctions = [ - debug, - error, image, markdown, metric, diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/debug.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/debug.tsx deleted file mode 100644 index 5870338ff6894..0000000000000 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/debug.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import ReactDOM from 'react-dom'; -import React from 'react'; -import { Debug } from '../../public/components/debug'; -import { RendererStrings } from '../../i18n'; -import { RendererFactory } from '../../types'; - -const { debug: strings } = RendererStrings; - -export const debug: RendererFactory = () => ({ - name: 'debug', - displayName: strings.getDisplayName(), - help: strings.getHelpDescription(), - reuseDomNode: true, - render(domNode, config, handlers) { - const renderDebug = () => ( -
- -
- ); - - ReactDOM.render(renderDebug(), domNode, () => handlers.done()); - - if (handlers.onResize) { - handlers.onResize(() => { - ReactDOM.render(renderDebug(), domNode, () => handlers.done()); - }); - } - - handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode)); - }, -}); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/error/error.scss b/x-pack/plugins/canvas/canvas_plugin_src/renderers/error/error.scss deleted file mode 100644 index 9229c1f88a096..0000000000000 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/error/error.scss +++ /dev/null @@ -1,16 +0,0 @@ -.canvasRenderError { - display: flex; - justify-content: center; - align-items: center; - - .canvasRenderError__icon { - opacity: .4; - stroke: $euiColorEmptyShade; - stroke-width: .2px; - - &:hover { - opacity: .6; - cursor: pointer; - } - } -} diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/error/index.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/error/index.tsx deleted file mode 100644 index dcf83c68f0c75..0000000000000 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/error/index.tsx +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import ReactDOM from 'react-dom'; -import React, { MouseEventHandler } from 'react'; -import { EuiIcon } from '@elastic/eui'; -import { Error } from '../../../public/components/error'; -import { Popover } from '../../../public/components/popover'; -import { RendererStrings } from '../../../i18n'; -import { RendererFactory } from '../../../types'; - -export interface Config { - error: Error; -} - -const { error: strings } = RendererStrings; - -export const error: RendererFactory = () => ({ - name: 'error', - displayName: strings.getDisplayName(), - help: strings.getHelpDescription(), - reuseDomNode: true, - render(domNode, config, handlers) { - const draw = () => { - const buttonSize = Math.min(domNode.clientHeight, domNode.clientWidth); - const button = (handleClick: MouseEventHandler) => ( - - ); - - ReactDOM.render( -
- {() => } -
, - - domNode, - () => handlers.done() - ); - }; - - draw(); - - handlers.onResize(draw); - - handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode)); - }, -}); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts index bf9b6a744e686..1d032aa829bc0 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts @@ -6,6 +6,7 @@ */ import { revealImageRenderer } from '../../../../../src/plugins/expression_reveal_image/public'; +import { errorRenderer, debugRenderer } from '../../../../../src/plugins/expression_error/public'; -export const renderFunctions = [revealImageRenderer]; +export const renderFunctions = [revealImageRenderer, errorRenderer, debugRenderer]; export const renderFunctionFactories = []; diff --git a/x-pack/plugins/canvas/i18n/renderers.ts b/x-pack/plugins/canvas/i18n/renderers.ts index 29687155818e7..fa1fbc063dbe6 100644 --- a/x-pack/plugins/canvas/i18n/renderers.ts +++ b/x-pack/plugins/canvas/i18n/renderers.ts @@ -55,16 +55,6 @@ export const RendererStrings = { defaultMessage: 'Renders an embeddable Saved Object from other parts of Kibana', }), }, - error: { - getDisplayName: () => - i18n.translate('xpack.canvas.renderer.error.displayName', { - defaultMessage: 'Error information', - }), - getHelpDescription: () => - i18n.translate('xpack.canvas.renderer.error.helpDescription', { - defaultMessage: 'Render error data in a way that is helpful to users', - }), - }, image: { getDisplayName: () => i18n.translate('xpack.canvas.renderer.image.displayName', { diff --git a/x-pack/plugins/canvas/kibana.json b/x-pack/plugins/canvas/kibana.json index 85d2e0709cb3e..545eae742a89e 100644 --- a/x-pack/plugins/canvas/kibana.json +++ b/x-pack/plugins/canvas/kibana.json @@ -10,6 +10,7 @@ "charts", "data", "embeddable", + "expressionError", "expressionRevealImage", "expressions", "features", diff --git a/x-pack/plugins/canvas/public/components/arg_add_popover/arg_add_popover.tsx b/x-pack/plugins/canvas/public/components/arg_add_popover/arg_add_popover.tsx index d9df1e4661fbf..e1cd5c55393fb 100644 --- a/x-pack/plugins/canvas/public/components/arg_add_popover/arg_add_popover.tsx +++ b/x-pack/plugins/canvas/public/components/arg_add_popover/arg_add_popover.tsx @@ -9,9 +9,7 @@ import React, { MouseEventHandler, FC } from 'react'; import PropTypes from 'prop-types'; import { EuiButtonIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; - -// @ts-expect-error untyped local -import { Popover, PopoverChildrenProps } from '../popover'; +import { Popover } from '../popover'; import { ArgAdd } from '../arg_add'; // @ts-expect-error untyped local import { Arg } from '../../expression_types/arg'; @@ -49,7 +47,7 @@ export const ArgAddPopover: FC = ({ options }) => { panelPaddingSize="none" button={button} > - {({ closePopover }: PopoverChildrenProps) => + {({ closePopover }) => options.map((opt) => ( )) .add('datasource with expression arguments', () => ( @@ -90,5 +91,6 @@ storiesOf('components/datasource/DatasourceComponent', module) setPreviewing={action('setPreviewing')} isInvalid={false} setInvalid={action('setInvalid')} + renderError={action('renderError')} /> )); diff --git a/x-pack/plugins/canvas/public/components/datasource/datasource_component.js b/x-pack/plugins/canvas/public/components/datasource/datasource_component.js index f09ce4c925820..c1df18fc06d55 100644 --- a/x-pack/plugins/canvas/public/components/datasource/datasource_component.js +++ b/x-pack/plugins/canvas/public/components/datasource/datasource_component.js @@ -60,6 +60,7 @@ export class DatasourceComponent extends PureComponent { setPreviewing: PropTypes.func, isInvalid: PropTypes.bool, setInvalid: PropTypes.func, + renderError: PropTypes.func, }; state = { defaultIndex: '' }; @@ -125,6 +126,7 @@ export class DatasourceComponent extends PureComponent { setPreviewing, isInvalid, setInvalid, + renderError, } = this.props; const { defaultIndex } = this.state; @@ -155,6 +157,7 @@ export class DatasourceComponent extends PureComponent { isInvalid, setInvalid, defaultIndex, + renderError, }); const hasExpressionArgs = Object.values(stateArgs).some((a) => a && typeof a[0] === 'object'); diff --git a/x-pack/plugins/canvas/public/components/datasource/datasource_preview/datasource_preview.js b/x-pack/plugins/canvas/public/components/datasource/datasource_preview/datasource_preview.js index 2eb42c5cb98dc..a45613f4bc96b 100644 --- a/x-pack/plugins/canvas/public/components/datasource/datasource_preview/datasource_preview.js +++ b/x-pack/plugins/canvas/public/components/datasource/datasource_preview/datasource_preview.js @@ -20,8 +20,11 @@ import { import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; +import { withSuspense } from '../../../../../../../src/plugins/presentation_util/public'; +import { LazyErrorComponent } from '../../../../../../../src/plugins/expression_error/public'; import { Datatable } from '../../datatable'; -import { Error } from '../../error'; + +const Error = withSuspense(LazyErrorComponent); const strings = { getEmptyFirstLineDescription: () => diff --git a/x-pack/plugins/canvas/public/components/expression_input/__stories__/__snapshots__/expression_input.stories.storyshot b/x-pack/plugins/canvas/public/components/expression_input/__stories__/__snapshots__/expression_input.stories.storyshot index 5c17eb2b68137..99d5dc3c115be 100644 --- a/x-pack/plugins/canvas/public/components/expression_input/__stories__/__snapshots__/expression_input.stories.storyshot +++ b/x-pack/plugins/canvas/public/components/expression_input/__stories__/__snapshots__/expression_input.stories.storyshot @@ -16,7 +16,18 @@ exports[`Storyshots components/ExpressionInput default 1`] = ` id="generated-id" onBlur={[Function]} onFocus={[Function]} - /> + > +
+
+
diff --git a/x-pack/plugins/canvas/public/components/home/__snapshots__/home.stories.storyshot b/x-pack/plugins/canvas/public/components/home/__snapshots__/home.stories.storyshot index 770e94ec4b174..02f54723abd42 100644 --- a/x-pack/plugins/canvas/public/components/home/__snapshots__/home.stories.storyshot +++ b/x-pack/plugins/canvas/public/components/home/__snapshots__/home.stories.storyshot @@ -123,9 +123,22 @@ exports[`Storyshots Home Home Page 1`] = `
- +
+
+ +
+
diff --git a/x-pack/plugins/canvas/public/components/popover/popover.tsx b/x-pack/plugins/canvas/public/components/popover/popover.tsx index 275d800fe2ca1..a2793b3759f1e 100644 --- a/x-pack/plugins/canvas/public/components/popover/popover.tsx +++ b/x-pack/plugins/canvas/public/components/popover/popover.tsx @@ -5,7 +5,6 @@ * 2.0. */ -/* eslint react/no-did-mount-set-state: 0, react/forbid-elements: 0 */ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { EuiPopover, EuiToolTip } from '@elastic/eui'; diff --git a/x-pack/plugins/canvas/public/components/workpad_header/edit_menu/edit_menu.component.tsx b/x-pack/plugins/canvas/public/components/workpad_header/edit_menu/edit_menu.component.tsx index efc7f2fae8f8b..f501410b26a74 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/edit_menu/edit_menu.component.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_header/edit_menu/edit_menu.component.tsx @@ -9,10 +9,9 @@ import React, { Fragment, FunctionComponent, useState } from 'react'; import PropTypes from 'prop-types'; import { EuiButtonEmpty, EuiContextMenu, EuiIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; - +import { Popover, ClosePopoverFn } from '../../popover'; import { ShortcutStrings } from '../../../../i18n/shortcuts'; import { flattenPanelTree } from '../../../lib/flatten_panel_tree'; -import { Popover, ClosePopoverFn } from '../../popover'; import { CustomElementModal } from '../../custom_element_modal'; import { CONTEXT_MENU_TOP_BORDER_CLASSNAME } from '../../../../common/lib/constants'; import { PositionedElement } from '../../../../types'; diff --git a/x-pack/plugins/canvas/public/components/workpad_header/element_menu/element_menu.component.tsx b/x-pack/plugins/canvas/public/components/workpad_header/element_menu/element_menu.component.tsx index e1d69163e0761..2907e8c4d5dd7 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/element_menu/element_menu.component.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_header/element_menu/element_menu.component.tsx @@ -15,12 +15,11 @@ import { EuiContextMenuPanelItemDescriptor, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; - +import { Popover, ClosePopoverFn } from '../../popover'; import { CONTEXT_MENU_TOP_BORDER_CLASSNAME } from '../../../../common/lib'; import { ElementSpec } from '../../../../types'; import { flattenPanelTree } from '../../../lib/flatten_panel_tree'; import { getId } from '../../../lib/get_id'; -import { Popover, ClosePopoverFn } from '../../popover'; import { AssetManager } from '../../asset_manager'; import { SavedElementsModal } from '../../saved_elements_modal'; diff --git a/x-pack/plugins/canvas/public/components/workpad_header/share_menu/share_menu.component.tsx b/x-pack/plugins/canvas/public/components/workpad_header/share_menu/share_menu.component.tsx index e59bf284258fc..de712b2698359 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/share_menu/share_menu.component.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_header/share_menu/share_menu.component.tsx @@ -9,12 +9,11 @@ import React, { FunctionComponent, useState } from 'react'; import PropTypes from 'prop-types'; import { EuiButtonEmpty, EuiContextMenu, EuiIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; - +import { Popover, ClosePopoverFn } from '../../popover'; import { ReportingStart } from '../../../../../reporting/public'; import { PDF, JSON } from '../../../../i18n/constants'; import { flattenPanelTree } from '../../../lib/flatten_panel_tree'; import { usePlatformService } from '../../../services'; -import { ClosePopoverFn, Popover } from '../../popover'; import { ShareWebsiteFlyout } from './flyout'; import { CanvasWorkpadSharingData, getPdfJobParams } from './utils'; diff --git a/x-pack/plugins/canvas/public/components/workpad_header/view_menu/view_menu.component.tsx b/x-pack/plugins/canvas/public/components/workpad_header/view_menu/view_menu.component.tsx index b001ad04caa44..b2c6d97a51748 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/view_menu/view_menu.component.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_header/view_menu/view_menu.component.tsx @@ -14,7 +14,7 @@ import { EuiContextMenuPanelItemDescriptor, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; - +import { Popover, ClosePopoverFn } from '../../popover'; import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL, @@ -22,7 +22,6 @@ import { } from '../../../../common/lib/constants'; import { flattenPanelTree } from '../../../lib/flatten_panel_tree'; -import { Popover, ClosePopoverFn } from '../../popover'; import { AutoRefreshControls } from './auto_refresh_controls'; import { KioskControls } from './kiosk_controls'; diff --git a/x-pack/plugins/canvas/public/style/index.scss b/x-pack/plugins/canvas/public/style/index.scss index aac898c3dd374..0860bfd5afe6a 100644 --- a/x-pack/plugins/canvas/public/style/index.scss +++ b/x-pack/plugins/canvas/public/style/index.scss @@ -19,7 +19,6 @@ @import '../components/datasource/datasource'; @import '../components/datasource/datasource_preview/datasource_preview'; @import '../components/datatable/datatable'; -@import '../components/debug/debug'; @import '../components/dom_preview/dom_preview'; @import '../components/element_card/element_card'; @import '../components/element_content/element_content'; diff --git a/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js b/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js index 60987e987f63a..df894a65afab1 100644 --- a/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js +++ b/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js @@ -5,8 +5,6 @@ * 2.0. */ -import { debug } from '../canvas_plugin_src/renderers/debug'; -import { error } from '../canvas_plugin_src/renderers/error'; import { image } from '../canvas_plugin_src/renderers/image'; import { repeatImage } from '../canvas_plugin_src/renderers/repeat_image'; import { markdown } from '../canvas_plugin_src/renderers/markdown'; @@ -18,6 +16,10 @@ import { shape } from '../canvas_plugin_src/renderers/shape'; import { table } from '../canvas_plugin_src/renderers/table'; import { text } from '../canvas_plugin_src/renderers/text'; import { revealImageRenderer as revealImage } from '../../../../src/plugins/expression_reveal_image/public'; +import { + errorRenderer as error, + debugRenderer as debug, +} from '../../../../src/plugins/expression_error/public'; /** * This is a collection of renderers which are bundled with the runtime. If diff --git a/x-pack/plugins/canvas/storybook/storyshots.test.tsx b/x-pack/plugins/canvas/storybook/storyshots.test.tsx index 84ac1a26281e0..ddb52a22d2f17 100644 --- a/x-pack/plugins/canvas/storybook/storyshots.test.tsx +++ b/x-pack/plugins/canvas/storybook/storyshots.test.tsx @@ -6,13 +6,15 @@ */ import fs from 'fs'; -import { ReactChildren } from 'react'; +import { ReactChildren, createElement } from 'react'; import path from 'path'; import moment from 'moment'; import 'moment-timezone'; import ReactDOM from 'react-dom'; +import { shallow } from 'enzyme'; +import { create, act } from 'react-test-renderer'; -import initStoryshots, { multiSnapshotWithOptions } from '@storybook/addon-storyshots'; +import initStoryshots, { Stories2SnapsConverter } from '@storybook/addon-storyshots'; // @ts-expect-error untyped library import styleSheetSerializer from 'jest-styled-components/src/styleSheetSerializer'; import { addSerializer } from 'jest-specific-snapshot'; @@ -114,11 +116,24 @@ jest.mock('../public/lib/es_service', () => ({ addSerializer(styleSheetSerializer); +const converter = new Stories2SnapsConverter(); + // Initialize Storyshots and build the Jest Snapshots initStoryshots({ configPath: path.resolve(__dirname), framework: 'react', - test: multiSnapshotWithOptions(), + // test: multiSnapshotWithOptions({}), + asyncJest: true, + test: async ({ story, context, done }) => { + const renderer = create(createElement(story.render)); + // wait until the element will perform all renders and resolve all promises (lazy loading, especially) + await act(() => new Promise((resolve) => setTimeout(resolve, 0))); + // save each snapshot to a different file (similar to "multiSnapshotWithOptions") + const snapshotFileName = converter.getSnapshotFileName(context); + expect(renderer).toMatchSpecificSnapshot(snapshotFileName); + done?.(); + }, // Don't snapshot tests that start with 'redux' storyNameRegex: /^((?!.*?redux).)*$/, + renderer: shallow, }); diff --git a/x-pack/plugins/canvas/tsconfig.json b/x-pack/plugins/canvas/tsconfig.json index 84581d7be85a3..bf9544a173f16 100644 --- a/x-pack/plugins/canvas/tsconfig.json +++ b/x-pack/plugins/canvas/tsconfig.json @@ -31,6 +31,7 @@ { "path": "../../../src/plugins/discover/tsconfig.json" }, { "path": "../../../src/plugins/embeddable/tsconfig.json" }, { "path": "../../../src/plugins/expressions/tsconfig.json" }, + { "path": "../../../src/plugins/expression_error/tsconfig.json" }, { "path": "../../../src/plugins/expression_reveal_image/tsconfig.json" }, { "path": "../../../src/plugins/home/tsconfig.json" }, { "path": "../../../src/plugins/inspector/tsconfig.json" }, diff --git a/x-pack/plugins/cases/docs/README.md b/x-pack/plugins/cases/docs/README.md index 85482d98dc509..16e9311958763 100644 --- a/x-pack/plugins/cases/docs/README.md +++ b/x-pack/plugins/cases/docs/README.md @@ -19,7 +19,7 @@ yarn global add typedoc typedoc-plugin-markdown ```bash cd x-pack/plugins/cases/docs -npx typedoc --options cases_client_typedoc.json +npx typedoc --gitRemote upstream --options cases_client_typedoc.json ``` After running the above commands the files in the `server` directory will be updated to match the new tsdocs. diff --git a/x-pack/plugins/cases/docs/cases_client/classes/client.casesclient.md b/x-pack/plugins/cases/docs/cases_client/classes/client.casesclient.md index bd07a44a2bfdf..b3bef7b36b571 100644 --- a/x-pack/plugins/cases/docs/cases_client/classes/client.casesclient.md +++ b/x-pack/plugins/cases/docs/cases_client/classes/client.casesclient.md @@ -45,7 +45,7 @@ Client wrapper that contains accessor methods for individual entities within the **Returns:** [*CasesClient*](client.casesclient.md) -Defined in: [client.ts:28](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/client.ts#L28) +Defined in: [client.ts:28](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/client.ts#L28) ## Properties @@ -53,7 +53,7 @@ Defined in: [client.ts:28](https://github.com/jonathan-buttner/kibana/blob/b65ed • `Private` `Readonly` **\_attachments**: [*AttachmentsSubClient*](../interfaces/attachments_client.attachmentssubclient.md) -Defined in: [client.ts:24](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/client.ts#L24) +Defined in: [client.ts:24](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/client.ts#L24) ___ @@ -61,7 +61,7 @@ ___ • `Private` `Readonly` **\_cases**: [*CasesSubClient*](../interfaces/cases_client.casessubclient.md) -Defined in: [client.ts:23](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/client.ts#L23) +Defined in: [client.ts:23](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/client.ts#L23) ___ @@ -69,7 +69,7 @@ ___ • `Private` `Readonly` **\_casesClientInternal**: *CasesClientInternal* -Defined in: [client.ts:22](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/client.ts#L22) +Defined in: [client.ts:22](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/client.ts#L22) ___ @@ -77,7 +77,7 @@ ___ • `Private` `Readonly` **\_configure**: [*ConfigureSubClient*](../interfaces/configure_client.configuresubclient.md) -Defined in: [client.ts:27](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/client.ts#L27) +Defined in: [client.ts:27](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/client.ts#L27) ___ @@ -85,7 +85,7 @@ ___ • `Private` `Readonly` **\_stats**: [*StatsSubClient*](../interfaces/stats_client.statssubclient.md) -Defined in: [client.ts:28](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/client.ts#L28) +Defined in: [client.ts:28](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/client.ts#L28) ___ @@ -93,7 +93,7 @@ ___ • `Private` `Readonly` **\_subCases**: [*SubCasesClient*](../interfaces/sub_cases_client.subcasesclient.md) -Defined in: [client.ts:26](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/client.ts#L26) +Defined in: [client.ts:26](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/client.ts#L26) ___ @@ -101,7 +101,7 @@ ___ • `Private` `Readonly` **\_userActions**: [*UserActionsSubClient*](../interfaces/user_actions_client.useractionssubclient.md) -Defined in: [client.ts:25](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/client.ts#L25) +Defined in: [client.ts:25](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/client.ts#L25) ## Accessors @@ -113,7 +113,7 @@ Retrieves an interface for interacting with attachments (comments) entities. **Returns:** [*AttachmentsSubClient*](../interfaces/attachments_client.attachmentssubclient.md) -Defined in: [client.ts:50](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/client.ts#L50) +Defined in: [client.ts:50](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/client.ts#L50) ___ @@ -125,7 +125,7 @@ Retrieves an interface for interacting with cases entities. **Returns:** [*CasesSubClient*](../interfaces/cases_client.casessubclient.md) -Defined in: [client.ts:43](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/client.ts#L43) +Defined in: [client.ts:43](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/client.ts#L43) ___ @@ -137,7 +137,7 @@ Retrieves an interface for interacting with the configuration of external connec **Returns:** [*ConfigureSubClient*](../interfaces/configure_client.configuresubclient.md) -Defined in: [client.ts:76](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/client.ts#L76) +Defined in: [client.ts:76](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/client.ts#L76) ___ @@ -149,7 +149,7 @@ Retrieves an interface for retrieving statistics related to the cases entities. **Returns:** [*StatsSubClient*](../interfaces/stats_client.statssubclient.md) -Defined in: [client.ts:83](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/client.ts#L83) +Defined in: [client.ts:83](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/client.ts#L83) ___ @@ -163,7 +163,7 @@ Currently this functionality is disabled and will throw an error if this functio **Returns:** [*SubCasesClient*](../interfaces/sub_cases_client.subcasesclient.md) -Defined in: [client.ts:66](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/client.ts#L66) +Defined in: [client.ts:66](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/client.ts#L66) ___ @@ -175,4 +175,4 @@ Retrieves an interface for interacting with the user actions associated with the **Returns:** [*UserActionsSubClient*](../interfaces/user_actions_client.useractionssubclient.md) -Defined in: [client.ts:57](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/client.ts#L57) +Defined in: [client.ts:57](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/client.ts#L57) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_add.addargs.md b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_add.addargs.md index f8f7babd15b90..0fafb377bcc41 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_add.addargs.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_add.addargs.md @@ -21,14 +21,14 @@ The arguments needed for creating a new attachment to a case. The case ID that this attachment will be associated with -Defined in: [attachments/add.ts:305](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/add.ts#L305) +Defined in: [attachments/add.ts:305](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/add.ts#L305) ___ ### comment -• **comment**: { `comment`: *string* ; `owner`: *string* ; `type`: user } \| { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } +• **comment**: { `comment`: *string* ; `owner`: *string* ; `type`: user } \| { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } \| { `actions`: { targets: { hostname: string; endpointId: string; }[]; type: string; } ; `comment`: *string* ; `owner`: *string* ; `type`: actions } The attachment values. -Defined in: [attachments/add.ts:309](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/add.ts#L309) +Defined in: [attachments/add.ts:309](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/add.ts#L309) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_client.attachmentssubclient.md b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_client.attachmentssubclient.md index 57141796f6f67..ff9744583cfaf 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_client.attachmentssubclient.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_client.attachmentssubclient.md @@ -35,7 +35,7 @@ Adds an attachment to a case. **Returns:** *Promise*<[*ICaseResponse*](typedoc_interfaces.icaseresponse.md)\> -Defined in: [attachments/client.ts:35](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/client.ts#L35) +Defined in: [attachments/client.ts:35](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/client.ts#L35) ___ @@ -53,7 +53,7 @@ Deletes a single attachment for a specific case. **Returns:** *Promise* -Defined in: [attachments/client.ts:43](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/client.ts#L43) +Defined in: [attachments/client.ts:43](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/client.ts#L43) ___ @@ -71,7 +71,7 @@ Deletes all attachments associated with a single case. **Returns:** *Promise* -Defined in: [attachments/client.ts:39](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/client.ts#L39) +Defined in: [attachments/client.ts:39](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/client.ts#L39) ___ @@ -89,13 +89,13 @@ Retrieves all comments matching the search criteria. **Returns:** *Promise*<[*ICommentsResponse*](typedoc_interfaces.icommentsresponse.md)\> -Defined in: [attachments/client.ts:47](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/client.ts#L47) +Defined in: [attachments/client.ts:47](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/client.ts#L47) ___ ### get -▸ **get**(`getArgs`: [*GetArgs*](attachments_get.getargs.md)): *Promise*<{ `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }\> +▸ **get**(`getArgs`: [*GetArgs*](attachments_get.getargs.md)): *Promise*<{ `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `actions`: { targets: { hostname: string; endpointId: string; }[]; type: string; } ; `comment`: *string* ; `owner`: *string* ; `type`: actions } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }\> Retrieves a single attachment for a case. @@ -105,9 +105,9 @@ Retrieves a single attachment for a case. | :------ | :------ | | `getArgs` | [*GetArgs*](attachments_get.getargs.md) | -**Returns:** *Promise*<{ `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }\> +**Returns:** *Promise*<{ `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `actions`: { targets: { hostname: string; endpointId: string; }[]; type: string; } ; `comment`: *string* ; `owner`: *string* ; `type`: actions } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }\> -Defined in: [attachments/client.ts:59](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/client.ts#L59) +Defined in: [attachments/client.ts:59](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/client.ts#L59) ___ @@ -125,7 +125,7 @@ Gets all attachments for a single case. **Returns:** *Promise*<[*IAllCommentsResponse*](typedoc_interfaces.iallcommentsresponse.md)\> -Defined in: [attachments/client.ts:55](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/client.ts#L55) +Defined in: [attachments/client.ts:55](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/client.ts#L55) ___ @@ -143,7 +143,7 @@ Retrieves all alerts attach to a case given a single case ID **Returns:** *Promise*<{ `attached_at`: *string* ; `id`: *string* ; `index`: *string* }[]\> -Defined in: [attachments/client.ts:51](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/client.ts#L51) +Defined in: [attachments/client.ts:51](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/client.ts#L51) ___ @@ -163,4 +163,4 @@ The request must include all fields for the attachment. Even the fields that are **Returns:** *Promise*<[*ICaseResponse*](typedoc_interfaces.icaseresponse.md)\> -Defined in: [attachments/client.ts:65](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/client.ts#L65) +Defined in: [attachments/client.ts:65](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/client.ts#L65) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_delete.deleteallargs.md b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_delete.deleteallargs.md index d134c92e282a3..39c72e81a9935 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_delete.deleteallargs.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_delete.deleteallargs.md @@ -21,7 +21,7 @@ Parameters for deleting all comments of a case or sub case. The case ID to delete all attachments for -Defined in: [attachments/delete.ts:31](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/delete.ts#L31) +Defined in: [attachments/delete.ts:31](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/delete.ts#L31) ___ @@ -31,4 +31,4 @@ ___ If specified the caseID will be ignored and this value will be used to find a sub case for deleting all the attachments -Defined in: [attachments/delete.ts:35](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/delete.ts#L35) +Defined in: [attachments/delete.ts:35](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/delete.ts#L35) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_delete.deleteargs.md b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_delete.deleteargs.md index a1c177bad8a09..fb7e61fa1521f 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_delete.deleteargs.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_delete.deleteargs.md @@ -22,7 +22,7 @@ Parameters for deleting a single attachment of a case or sub case. The attachment ID to delete -Defined in: [attachments/delete.ts:49](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/delete.ts#L49) +Defined in: [attachments/delete.ts:49](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/delete.ts#L49) ___ @@ -32,7 +32,7 @@ ___ The case ID to delete an attachment from -Defined in: [attachments/delete.ts:45](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/delete.ts#L45) +Defined in: [attachments/delete.ts:45](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/delete.ts#L45) ___ @@ -42,4 +42,4 @@ ___ If specified the caseID will be ignored and this value will be used to find a sub case for deleting the attachment -Defined in: [attachments/delete.ts:53](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/delete.ts#L53) +Defined in: [attachments/delete.ts:53](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/delete.ts#L53) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.findargs.md b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.findargs.md index dcd4deb28b687..826a05f5865ab 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.findargs.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.findargs.md @@ -21,7 +21,7 @@ Parameters for finding attachments of a case The case ID for finding associated attachments -Defined in: [attachments/get.ts:47](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/get.ts#L47) +Defined in: [attachments/get.ts:47](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/get.ts#L47) ___ @@ -48,4 +48,4 @@ Optional parameters for filtering the returned attachments | `sortOrder` | *undefined* \| ``"desc"`` \| ``"asc"`` | | `subCaseId` | *undefined* \| *string* | -Defined in: [attachments/get.ts:51](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/get.ts#L51) +Defined in: [attachments/get.ts:51](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/get.ts#L51) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getallalertsattachtocase.md b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getallalertsattachtocase.md index d935823054b03..abeeaca19b23e 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getallalertsattachtocase.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getallalertsattachtocase.md @@ -18,4 +18,4 @@ The ID of the case to retrieve the alerts from -Defined in: [attachments/get.ts:87](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/get.ts#L87) +Defined in: [attachments/get.ts:87](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/get.ts#L87) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getallargs.md b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getallargs.md index 9577e89b46074..9ea29437d5c2c 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getallargs.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getallargs.md @@ -22,7 +22,7 @@ Parameters for retrieving all attachments of a case The case ID to retrieve all attachments for -Defined in: [attachments/get.ts:61](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/get.ts#L61) +Defined in: [attachments/get.ts:61](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/get.ts#L61) ___ @@ -32,7 +32,7 @@ ___ Optionally include the attachments associated with a sub case -Defined in: [attachments/get.ts:65](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/get.ts#L65) +Defined in: [attachments/get.ts:65](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/get.ts#L65) ___ @@ -42,4 +42,4 @@ ___ If included the case ID will be ignored and the attachments will be retrieved from the specified ID of the sub case -Defined in: [attachments/get.ts:69](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/get.ts#L69) +Defined in: [attachments/get.ts:69](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/get.ts#L69) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getargs.md b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getargs.md index 5530ad8bd936e..e46d83d795f48 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getargs.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getargs.md @@ -19,7 +19,7 @@ The ID of the attachment to retrieve -Defined in: [attachments/get.ts:80](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/get.ts#L80) +Defined in: [attachments/get.ts:80](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/get.ts#L80) ___ @@ -29,4 +29,4 @@ ___ The ID of the case to retrieve an attachment from -Defined in: [attachments/get.ts:76](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/get.ts#L76) +Defined in: [attachments/get.ts:76](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/get.ts#L76) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_update.updateargs.md b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_update.updateargs.md index ce586a6bfdfbd..23d7c88c9c864 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_update.updateargs.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_update.updateargs.md @@ -22,7 +22,7 @@ Parameters for updating a single attachment The ID of the case that is associated with this attachment -Defined in: [attachments/update.ts:32](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/update.ts#L32) +Defined in: [attachments/update.ts:32](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/update.ts#L32) ___ @@ -32,14 +32,14 @@ ___ The ID of a sub case, if specified a sub case will be searched for to perform the attachment update instead of on a case -Defined in: [attachments/update.ts:40](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/update.ts#L40) +Defined in: [attachments/update.ts:40](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/update.ts#L40) ___ ### updateRequest -• **updateRequest**: { `comment`: *string* ; `owner`: *string* ; `type`: user } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `id`: *string* ; `version`: *string* } +• **updateRequest**: { `comment`: *string* ; `owner`: *string* ; `type`: user } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `id`: *string* ; `version`: *string* } & { `actions`: { targets: { hostname: string; endpointId: string; }[]; type: string; } ; `comment`: *string* ; `owner`: *string* ; `type`: actions } & { `id`: *string* ; `version`: *string* } The full attachment request with the fields updated with appropriate values -Defined in: [attachments/update.ts:36](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/update.ts#L36) +Defined in: [attachments/update.ts:36](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/update.ts#L36) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/cases_client.casessubclient.md b/x-pack/plugins/cases/docs/cases_client/interfaces/cases_client.casessubclient.md index 52cf2fbaf1ef1..45285066b4608 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/cases_client.casessubclient.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/cases_client.casessubclient.md @@ -36,7 +36,7 @@ Creates a case. **Returns:** *Promise*<[*ICaseResponse*](typedoc_interfaces.icaseresponse.md)\> -Defined in: [cases/client.ts:49](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/client.ts#L49) +Defined in: [cases/client.ts:49](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/client.ts#L49) ___ @@ -56,7 +56,7 @@ Delete a case and all its comments. **Returns:** *Promise* -Defined in: [cases/client.ts:73](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/client.ts#L73) +Defined in: [cases/client.ts:73](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/client.ts#L73) ___ @@ -76,7 +76,7 @@ If the `owner` field is left empty then all the cases that the user has access t **Returns:** *Promise*<[*ICasesFindResponse*](typedoc_interfaces.icasesfindresponse.md)\> -Defined in: [cases/client.ts:55](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/client.ts#L55) +Defined in: [cases/client.ts:55](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/client.ts#L55) ___ @@ -94,7 +94,7 @@ Retrieves a single case with the specified ID. **Returns:** *Promise*<[*ICaseResponse*](typedoc_interfaces.icaseresponse.md)\> -Defined in: [cases/client.ts:59](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/client.ts#L59) +Defined in: [cases/client.ts:59](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/client.ts#L59) ___ @@ -112,7 +112,7 @@ Retrieves the cases ID and title that have the requested alert attached to them **Returns:** *Promise*<{ `id`: *string* ; `title`: *string* }[]\> -Defined in: [cases/client.ts:85](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/client.ts#L85) +Defined in: [cases/client.ts:85](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/client.ts#L85) ___ @@ -131,7 +131,7 @@ Retrieves all the reporters across all accessible cases. **Returns:** *Promise*<{ `email`: *undefined* \| ``null`` \| *string* ; `full_name`: *undefined* \| ``null`` \| *string* ; `username`: *undefined* \| ``null`` \| *string* }[]\> -Defined in: [cases/client.ts:81](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/client.ts#L81) +Defined in: [cases/client.ts:81](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/client.ts#L81) ___ @@ -150,7 +150,7 @@ Retrieves all the tags across all cases the user making the request has access t **Returns:** *Promise* -Defined in: [cases/client.ts:77](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/client.ts#L77) +Defined in: [cases/client.ts:77](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/client.ts#L77) ___ @@ -168,7 +168,7 @@ Pushes a specific case to an external system. **Returns:** *Promise*<[*ICaseResponse*](typedoc_interfaces.icaseresponse.md)\> -Defined in: [cases/client.ts:63](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/client.ts#L63) +Defined in: [cases/client.ts:63](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/client.ts#L63) ___ @@ -186,4 +186,4 @@ Update the specified cases with the passed in values. **Returns:** *Promise*<[*ICasesResponse*](typedoc_interfaces.icasesresponse.md)\> -Defined in: [cases/client.ts:67](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/client.ts#L67) +Defined in: [cases/client.ts:67](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/client.ts#L67) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/cases_get.casesbyalertidparams.md b/x-pack/plugins/cases/docs/cases_client/interfaces/cases_get.casesbyalertidparams.md index 4992ed035721b..257269ca64566 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/cases_get.casesbyalertidparams.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/cases_get.casesbyalertidparams.md @@ -21,7 +21,7 @@ Parameters for finding cases IDs using an alert ID The alert ID to search for -Defined in: [cases/get.ts:44](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/get.ts#L44) +Defined in: [cases/get.ts:44](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/get.ts#L44) ___ @@ -37,4 +37,4 @@ The filtering options when searching for associated cases. | :------ | :------ | | `owner` | *undefined* \| *string* \| *string*[] | -Defined in: [cases/get.ts:48](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/get.ts#L48) +Defined in: [cases/get.ts:48](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/get.ts#L48) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/cases_get.getparams.md b/x-pack/plugins/cases/docs/cases_client/interfaces/cases_get.getparams.md index a4dfc7301e543..16cc952746818 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/cases_get.getparams.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/cases_get.getparams.md @@ -22,7 +22,7 @@ The parameters for retrieving a case Case ID -Defined in: [cases/get.ts:145](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/get.ts#L145) +Defined in: [cases/get.ts:145](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/get.ts#L145) ___ @@ -32,7 +32,7 @@ ___ Whether to include the attachments for a case in the response -Defined in: [cases/get.ts:149](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/get.ts#L149) +Defined in: [cases/get.ts:149](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/get.ts#L149) ___ @@ -42,4 +42,4 @@ ___ Whether to include the attachments for all children of a case in the response -Defined in: [cases/get.ts:153](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/get.ts#L153) +Defined in: [cases/get.ts:153](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/get.ts#L153) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/cases_push.pushparams.md b/x-pack/plugins/cases/docs/cases_client/interfaces/cases_push.pushparams.md index 0ed510700af8a..3aa6ee77941a8 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/cases_push.pushparams.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/cases_push.pushparams.md @@ -21,7 +21,7 @@ Parameters for pushing a case to an external system The ID of a case -Defined in: [cases/push.ts:53](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/push.ts#L53) +Defined in: [cases/push.ts:53](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/push.ts#L53) ___ @@ -31,4 +31,4 @@ ___ The ID of an external system to push to -Defined in: [cases/push.ts:57](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/push.ts#L57) +Defined in: [cases/push.ts:57](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/push.ts#L57) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/configure_client.configuresubclient.md b/x-pack/plugins/cases/docs/cases_client/interfaces/configure_client.configuresubclient.md index 98a6c3a2fcbbf..f94e7d6c7f49e 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/configure_client.configuresubclient.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/configure_client.configuresubclient.md @@ -31,7 +31,7 @@ Creates a configuration if one does not already exist. If one exists it is delet **Returns:** *Promise*<[*ICasesConfigureResponse*](typedoc_interfaces.icasesconfigureresponse.md)\> -Defined in: [configure/client.ts:98](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/configure/client.ts#L98) +Defined in: [configure/client.ts:98](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/configure/client.ts#L98) ___ @@ -50,7 +50,7 @@ Retrieves the external connector configuration for a particular case owner. **Returns:** *Promise*<{} \| [*ICasesConfigureResponse*](typedoc_interfaces.icasesconfigureresponse.md)\> -Defined in: [configure/client.ts:80](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/configure/client.ts#L80) +Defined in: [configure/client.ts:80](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/configure/client.ts#L80) ___ @@ -62,7 +62,7 @@ Retrieves the valid external connectors supported by the cases plugin. **Returns:** *Promise* -Defined in: [configure/client.ts:84](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/configure/client.ts#L84) +Defined in: [configure/client.ts:84](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/configure/client.ts#L84) ___ @@ -81,4 +81,4 @@ Updates a particular configuration with new values. **Returns:** *Promise*<[*ICasesConfigureResponse*](typedoc_interfaces.icasesconfigureresponse.md)\> -Defined in: [configure/client.ts:91](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/configure/client.ts#L91) +Defined in: [configure/client.ts:91](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/configure/client.ts#L91) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/stats_client.statssubclient.md b/x-pack/plugins/cases/docs/cases_client/interfaces/stats_client.statssubclient.md index cc0f30055597d..9bbf3cdaf015f 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/stats_client.statssubclient.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/stats_client.statssubclient.md @@ -29,4 +29,4 @@ Retrieves the total number of open, closed, and in-progress cases. **Returns:** *Promise*<{ `count_closed_cases`: *number* ; `count_in_progress_cases`: *number* ; `count_open_cases`: *number* }\> -Defined in: [stats/client.ts:34](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/stats/client.ts#L34) +Defined in: [stats/client.ts:34](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/stats/client.ts#L34) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/sub_cases_client.subcasesclient.md b/x-pack/plugins/cases/docs/cases_client/interfaces/sub_cases_client.subcasesclient.md index 5c0369709c0f0..e3ca5a756f8a7 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/sub_cases_client.subcasesclient.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/sub_cases_client.subcasesclient.md @@ -31,7 +31,7 @@ Deletes the specified entities and their attachments. **Returns:** *Promise* -Defined in: [sub_cases/client.ts:68](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/sub_cases/client.ts#L68) +Defined in: [sub_cases/client.ts:68](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/sub_cases/client.ts#L68) ___ @@ -49,7 +49,7 @@ Retrieves the sub cases matching the search criteria. **Returns:** *Promise*<[*ISubCasesFindResponse*](typedoc_interfaces.isubcasesfindresponse.md)\> -Defined in: [sub_cases/client.ts:72](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/sub_cases/client.ts#L72) +Defined in: [sub_cases/client.ts:72](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/sub_cases/client.ts#L72) ___ @@ -67,7 +67,7 @@ Retrieves a single sub case. **Returns:** *Promise*<[*ISubCaseResponse*](typedoc_interfaces.isubcaseresponse.md)\> -Defined in: [sub_cases/client.ts:76](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/sub_cases/client.ts#L76) +Defined in: [sub_cases/client.ts:76](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/sub_cases/client.ts#L76) ___ @@ -86,4 +86,4 @@ Updates the specified sub cases to the new values included in the request. **Returns:** *Promise*<[*ISubCasesResponse*](typedoc_interfaces.isubcasesresponse.md)\> -Defined in: [sub_cases/client.ts:80](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/sub_cases/client.ts#L80) +Defined in: [sub_cases/client.ts:80](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/sub_cases/client.ts#L80) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasepostrequest.md b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasepostrequest.md index 70533a15fe616..5f9189ea41e48 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasepostrequest.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasepostrequest.md @@ -29,7 +29,7 @@ the docs are huge. ### connector -• **connector**: { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { issueType: string \| null; priority: string \| null; parent: string \| null; } ; `type`: jira } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { incidentTypes: string[] \| null; severityCode: string \| null; } ; `type`: resilient } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { impact: string \| null; severity: string \| null; urgency: string \| null; category: string \| null; subcategory: string \| null; } ; `type`: serviceNowITSM } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { category: string \| null; destIp: boolean \| null; malwareHash: boolean \| null; malwareUrl: boolean \| null; priority: string \| null; sourceIp: boolean \| null; subcategory: string \| null; } ; `type`: serviceNowSIR } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` ; `type`: none } +• **connector**: { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { issueType: string \| null; priority: string \| null; parent: string \| null; } ; `type`: jira } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` ; `type`: none } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { incidentTypes: string[] \| null; severityCode: string \| null; } ; `type`: resilient } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { impact: string \| null; severity: string \| null; urgency: string \| null; category: string \| null; subcategory: string \| null; } ; `type`: serviceNowITSM } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { category: string \| null; destIp: boolean \| null; malwareHash: boolean \| null; malwareUrl: boolean \| null; priority: string \| null; sourceIp: boolean \| null; subcategory: string \| null; } ; `type`: serviceNowSIR } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { caseId: string \| null; } ; `type`: swimlane } Inherited from: CasePostRequest.connector diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icaseresponse.md b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icaseresponse.md index 5db55e5552473..dc591a508844b 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icaseresponse.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icaseresponse.md @@ -57,7 +57,7 @@ ___ ### comments -• **comments**: *undefined* \| { `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }[] +• **comments**: *undefined* \| { `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `actions`: { targets: { hostname: string; endpointId: string; }[]; type: string; } ; `comment`: *string* ; `owner`: *string* ; `type`: actions } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }[] Inherited from: CaseResponse.comments @@ -65,7 +65,7 @@ ___ ### connector -• **connector**: { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { issueType: string \| null; priority: string \| null; parent: string \| null; } ; `type`: jira } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { incidentTypes: string[] \| null; severityCode: string \| null; } ; `type`: resilient } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { impact: string \| null; severity: string \| null; urgency: string \| null; category: string \| null; subcategory: string \| null; } ; `type`: serviceNowITSM } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { category: string \| null; destIp: boolean \| null; malwareHash: boolean \| null; malwareUrl: boolean \| null; priority: string \| null; sourceIp: boolean \| null; subcategory: string \| null; } ; `type`: serviceNowSIR } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` ; `type`: none } +• **connector**: { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { issueType: string \| null; priority: string \| null; parent: string \| null; } ; `type`: jira } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` ; `type`: none } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { incidentTypes: string[] \| null; severityCode: string \| null; } ; `type`: resilient } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { impact: string \| null; severity: string \| null; urgency: string \| null; category: string \| null; subcategory: string \| null; } ; `type`: serviceNowITSM } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { category: string \| null; destIp: boolean \| null; malwareHash: boolean \| null; malwareUrl: boolean \| null; priority: string \| null; sourceIp: boolean \| null; subcategory: string \| null; } ; `type`: serviceNowSIR } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { caseId: string \| null; } ; `type`: swimlane } Inherited from: CaseResponse.connector @@ -159,7 +159,7 @@ ___ ### subCases -• **subCases**: *undefined* \| { `status`: CaseStatuses } & { `closed_at`: ``null`` \| *string* ; `closed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `created_at`: *string* ; `created_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `totalAlerts`: *number* ; `totalComment`: *number* ; `version`: *string* } & { `comments`: *undefined* \| { `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }[] }[] +• **subCases**: *undefined* \| { `status`: CaseStatuses } & { `closed_at`: ``null`` \| *string* ; `closed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `created_at`: *string* ; `created_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `totalAlerts`: *number* ; `totalComment`: *number* ; `version`: *string* } & { `comments`: *undefined* \| { `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `actions`: { targets: { hostname: string; endpointId: string; }[]; type: string; } ; `comment`: *string* ; `owner`: *string* ; `type`: actions } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }[] }[] Inherited from: CaseResponse.subCases diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesconfigurepatch.md b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesconfigurepatch.md index 3854fda03fb6a..9ab5341a2dbc6 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesconfigurepatch.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesconfigurepatch.md @@ -30,7 +30,7 @@ ___ ### connector -• **connector**: *undefined* \| { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { issueType: string \| null; priority: string \| null; parent: string \| null; } ; `type`: jira } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { incidentTypes: string[] \| null; severityCode: string \| null; } ; `type`: resilient } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { impact: string \| null; severity: string \| null; urgency: string \| null; category: string \| null; subcategory: string \| null; } ; `type`: serviceNowITSM } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { category: string \| null; destIp: boolean \| null; malwareHash: boolean \| null; malwareUrl: boolean \| null; priority: string \| null; sourceIp: boolean \| null; subcategory: string \| null; } ; `type`: serviceNowSIR } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` ; `type`: none } +• **connector**: *undefined* \| { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { issueType: string \| null; priority: string \| null; parent: string \| null; } ; `type`: jira } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` ; `type`: none } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { incidentTypes: string[] \| null; severityCode: string \| null; } ; `type`: resilient } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { impact: string \| null; severity: string \| null; urgency: string \| null; category: string \| null; subcategory: string \| null; } ; `type`: serviceNowITSM } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { category: string \| null; destIp: boolean \| null; malwareHash: boolean \| null; malwareUrl: boolean \| null; priority: string \| null; sourceIp: boolean \| null; subcategory: string \| null; } ; `type`: serviceNowSIR } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { caseId: string \| null; } ; `type`: swimlane } Inherited from: CasesConfigurePatch.connector diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesconfigurerequest.md b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesconfigurerequest.md index 548e1a5c48f58..0b1c11ac548a6 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesconfigurerequest.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesconfigurerequest.md @@ -30,7 +30,7 @@ ___ ### connector -• **connector**: { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { issueType: string \| null; priority: string \| null; parent: string \| null; } ; `type`: jira } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { incidentTypes: string[] \| null; severityCode: string \| null; } ; `type`: resilient } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { impact: string \| null; severity: string \| null; urgency: string \| null; category: string \| null; subcategory: string \| null; } ; `type`: serviceNowITSM } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { category: string \| null; destIp: boolean \| null; malwareHash: boolean \| null; malwareUrl: boolean \| null; priority: string \| null; sourceIp: boolean \| null; subcategory: string \| null; } ; `type`: serviceNowSIR } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` ; `type`: none } +• **connector**: { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { issueType: string \| null; priority: string \| null; parent: string \| null; } ; `type`: jira } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` ; `type`: none } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { incidentTypes: string[] \| null; severityCode: string \| null; } ; `type`: resilient } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { impact: string \| null; severity: string \| null; urgency: string \| null; category: string \| null; subcategory: string \| null; } ; `type`: serviceNowITSM } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { category: string \| null; destIp: boolean \| null; malwareHash: boolean \| null; malwareUrl: boolean \| null; priority: string \| null; sourceIp: boolean \| null; subcategory: string \| null; } ; `type`: serviceNowSIR } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { caseId: string \| null; } ; `type`: swimlane } Inherited from: CasesConfigureRequest.connector diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesconfigureresponse.md b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesconfigureresponse.md index c493a4c6c0f0c..42c7378431c1b 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesconfigureresponse.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesconfigureresponse.md @@ -38,7 +38,7 @@ ___ ### connector -• **connector**: { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { issueType: string \| null; priority: string \| null; parent: string \| null; } ; `type`: jira } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { incidentTypes: string[] \| null; severityCode: string \| null; } ; `type`: resilient } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { impact: string \| null; severity: string \| null; urgency: string \| null; category: string \| null; subcategory: string \| null; } ; `type`: serviceNowITSM } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { category: string \| null; destIp: boolean \| null; malwareHash: boolean \| null; malwareUrl: boolean \| null; priority: string \| null; sourceIp: boolean \| null; subcategory: string \| null; } ; `type`: serviceNowSIR } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` ; `type`: none } +• **connector**: { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { issueType: string \| null; priority: string \| null; parent: string \| null; } ; `type`: jira } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` ; `type`: none } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { incidentTypes: string[] \| null; severityCode: string \| null; } ; `type`: resilient } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { impact: string \| null; severity: string \| null; urgency: string \| null; category: string \| null; subcategory: string \| null; } ; `type`: serviceNowITSM } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { category: string \| null; destIp: boolean \| null; malwareHash: boolean \| null; malwareUrl: boolean \| null; priority: string \| null; sourceIp: boolean \| null; subcategory: string \| null; } ; `type`: serviceNowSIR } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { caseId: string \| null; } ; `type`: swimlane } Inherited from: CasesConfigureResponse.connector diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesfindresponse.md b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesfindresponse.md index 9be5fd5743a8e..06e14d219cde0 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesfindresponse.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesfindresponse.md @@ -26,7 +26,7 @@ ### cases -• **cases**: { `connector`: { id: string; name: string; } & { type: ConnectorTypes.jira; fields: { issueType: string \| null; priority: string \| null; parent: string \| null; } \| null; } & { id: string; name: string; } & { type: ConnectorTypes.resilient; fields: { incidentTypes: string[] \| null; severityCode: string \| null; } \| null; } & { id: string; name: string; } & { type: ConnectorTypes.serviceNowITSM; fields: { impact: string \| null; severity: string \| null; urgency: string \| null; category: string \| null; subcategory: string \| null; } \| null; } & { id: string; name: string; } & { type: ConnectorTypes.serviceNowSIR; fields: { category: string \| null; destIp: boolean \| null; malwareHash: boolean \| null; malwareUrl: boolean \| null; priority: string \| null; sourceIp: boolean \| null; subcategory: string \| null; } \| null; } & { id: string; name: string; } & { type: ConnectorTypes.none; fields: null; } ; `description`: *string* ; `owner`: *string* ; `settings`: { syncAlerts: boolean; } ; `status`: CaseStatuses ; `tags`: *string*[] ; `title`: *string* ; `type`: CaseType } & { `closed_at`: ``null`` \| *string* ; `closed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `external_service`: ``null`` \| { connector\_id: string; connector\_name: string; external\_id: string; external\_title: string; external\_url: string; } & { pushed\_at: string; pushed\_by: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; }; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `totalAlerts`: *number* ; `totalComment`: *number* ; `version`: *string* } & { `comments`: *undefined* \| { `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }[] ; `subCaseIds`: *undefined* \| *string*[] ; `subCases`: *undefined* \| { `status`: CaseStatuses } & { `closed_at`: ``null`` \| *string* ; `closed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `created_at`: *string* ; `created_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `totalAlerts`: *number* ; `totalComment`: *number* ; `version`: *string* } & { comments?: ((({ comment: string; type: CommentType.user; owner: string; } & { associationType: AssociationType; created\_at: string; created\_by: { email: string \| null \| undefined; full\_name: string \| ... 1 more ... \| undefined; username: string \| ... 1 more ... \| undefined; }; ... 4 more ...; updated\_by: { ...; } ...[] }[] +• **cases**: { `connector`: { id: string; name: string; } & { type: ConnectorTypes.jira; fields: { issueType: string \| null; priority: string \| null; parent: string \| null; } \| null; } & { id: string; name: string; } & { type: ConnectorTypes.none; fields: null; } & { id: string; name: string; } & { type: ConnectorTypes.resilient; fields: { incidentTypes: string[] \| null; severityCode: string \| null; } \| null; } & { id: string; name: string; } & { type: ConnectorTypes.serviceNowITSM; fields: { impact: string \| null; severity: string \| null; urgency: string \| null; category: string \| null; subcategory: string \| null; } \| null; } & { id: string; name: string; } & { type: ConnectorTypes.serviceNowSIR; fields: { category: string \| null; destIp: boolean \| null; malwareHash: boolean \| null; malwareUrl: boolean \| null; priority: string \| null; sourceIp: boolean \| null; subcategory: string \| null; } \| null; } & { id: string; name: string; } & { type: ConnectorTypes.swimlane; fields: { caseId: string \| null; } \| null; } ; `description`: *string* ; `owner`: *string* ; `settings`: { syncAlerts: boolean; } ; `status`: CaseStatuses ; `tags`: *string*[] ; `title`: *string* ; `type`: CaseType } & { `closed_at`: ``null`` \| *string* ; `closed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `external_service`: ``null`` \| { connector\_id: string; connector\_name: string; external\_id: string; external\_title: string; external\_url: string; } & { pushed\_at: string; pushed\_by: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; }; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `totalAlerts`: *number* ; `totalComment`: *number* ; `version`: *string* } & { `comments`: *undefined* \| { `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `actions`: { targets: { hostname: string; endpointId: string; }[]; type: string; } ; `comment`: *string* ; `owner`: *string* ; `type`: actions } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }[] ; `subCaseIds`: *undefined* \| *string*[] ; `subCases`: *undefined* \| { `status`: CaseStatuses } & { `closed_at`: ``null`` \| *string* ; `closed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `created_at`: *string* ; `created_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `totalAlerts`: *number* ; `totalComment`: *number* ; `version`: *string* } & { comments?: ((({ comment: string; type: CommentType.user; owner: string; } & { associationType: AssociationType; created\_at: string; created\_by: { email: string \| null \| undefined; full\_name: string \| ... 1 more ... \| undefined; username: string \| ... 1 more ... \| undefined; }; ... 4 more ...; updated\_by: { ...; } ...[] }[] Inherited from: CasesFindResponse.cases diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasespatchrequest.md b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasespatchrequest.md index bfdb3b7315e55..d4747a1836cc4 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasespatchrequest.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasespatchrequest.md @@ -20,6 +20,6 @@ ### cases -• **cases**: { `connector`: *undefined* \| { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { issueType: string \| null; priority: string \| null; parent: string \| null; } ; `type`: jira } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { incidentTypes: string[] \| null; severityCode: string \| null; } ; `type`: resilient } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { impact: string \| null; severity: string \| null; urgency: string \| null; category: string \| null; subcategory: string \| null; } ; `type`: serviceNowITSM } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { category: string \| null; destIp: boolean \| null; malwareHash: boolean \| null; malwareUrl: boolean \| null; priority: string \| null; sourceIp: boolean \| null; subcategory: string \| null; } ; `type`: serviceNowSIR } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` ; `type`: none } ; `description`: *undefined* \| *string* ; `owner`: *undefined* \| *string* ; `settings`: *undefined* \| { `syncAlerts`: *boolean* } ; `status`: *undefined* \| open \| *any*[*any*] \| closed ; `tags`: *undefined* \| *string*[] ; `title`: *undefined* \| *string* ; `type`: *undefined* \| collection \| individual } & { `id`: *string* ; `version`: *string* }[] +• **cases**: { `connector`: *undefined* \| { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { issueType: string \| null; priority: string \| null; parent: string \| null; } ; `type`: jira } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` ; `type`: none } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { incidentTypes: string[] \| null; severityCode: string \| null; } ; `type`: resilient } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { impact: string \| null; severity: string \| null; urgency: string \| null; category: string \| null; subcategory: string \| null; } ; `type`: serviceNowITSM } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { category: string \| null; destIp: boolean \| null; malwareHash: boolean \| null; malwareUrl: boolean \| null; priority: string \| null; sourceIp: boolean \| null; subcategory: string \| null; } ; `type`: serviceNowSIR } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { caseId: string \| null; } ; `type`: swimlane } ; `description`: *undefined* \| *string* ; `owner`: *undefined* \| *string* ; `settings`: *undefined* \| { `syncAlerts`: *boolean* } ; `status`: *undefined* \| open \| *any*[*any*] \| closed ; `tags`: *undefined* \| *string*[] ; `title`: *undefined* \| *string* ; `type`: *undefined* \| collection \| individual } & { `id`: *string* ; `version`: *string* }[] Inherited from: CasesPatchRequest.cases diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icommentsresponse.md b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icommentsresponse.md index d34480b2c633c..4a720e2c6d9be 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icommentsresponse.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icommentsresponse.md @@ -23,7 +23,7 @@ ### comments -• **comments**: { `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }[] +• **comments**: { `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `actions`: { targets: { hostname: string; endpointId: string; }[]; type: string; } ; `comment`: *string* ; `owner`: *string* ; `type`: actions } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }[] Inherited from: CommentsResponse.comments diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.isubcaseresponse.md b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.isubcaseresponse.md index b33b280d2e753..a6bf610f86349 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.isubcaseresponse.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.isubcaseresponse.md @@ -48,7 +48,7 @@ ___ ### comments -• **comments**: *undefined* \| { `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }[] +• **comments**: *undefined* \| { `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `actions`: { targets: { hostname: string; endpointId: string; }[]; type: string; } ; `comment`: *string* ; `owner`: *string* ; `type`: actions } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }[] Inherited from: SubCaseResponse.comments diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.isubcasesfindresponse.md b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.isubcasesfindresponse.md index 35d63126f608a..61fb60a54db00 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.isubcasesfindresponse.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.isubcasesfindresponse.md @@ -66,7 +66,7 @@ ___ ### subCases -• **subCases**: { `status`: CaseStatuses } & { `closed_at`: ``null`` \| *string* ; `closed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `created_at`: *string* ; `created_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `totalAlerts`: *number* ; `totalComment`: *number* ; `version`: *string* } & { `comments`: *undefined* \| { `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }[] }[] +• **subCases**: { `status`: CaseStatuses } & { `closed_at`: ``null`` \| *string* ; `closed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `created_at`: *string* ; `created_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `totalAlerts`: *number* ; `totalComment`: *number* ; `version`: *string* } & { `comments`: *undefined* \| { `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `actions`: { targets: { hostname: string; endpointId: string; }[]; type: string; } ; `comment`: *string* ; `owner`: *string* ; `type`: actions } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }[] }[] Inherited from: SubCasesFindResponse.subCases diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/user_actions_client.useractionget.md b/x-pack/plugins/cases/docs/cases_client/interfaces/user_actions_client.useractionget.md index 5f0cc89239fd8..1cbebef379dbd 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/user_actions_client.useractionget.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/user_actions_client.useractionget.md @@ -21,7 +21,7 @@ Parameters for retrieving user actions for a particular case The ID of the case -Defined in: [user_actions/client.ts:19](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/user_actions/client.ts#L19) +Defined in: [user_actions/client.ts:19](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/user_actions/client.ts#L19) ___ @@ -31,4 +31,4 @@ ___ If specified then a sub case will be used for finding all the user actions -Defined in: [user_actions/client.ts:23](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/user_actions/client.ts#L23) +Defined in: [user_actions/client.ts:23](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/user_actions/client.ts#L23) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/user_actions_client.useractionssubclient.md b/x-pack/plugins/cases/docs/cases_client/interfaces/user_actions_client.useractionssubclient.md index df2641adf5a8c..065f20b4cefcb 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/user_actions_client.useractionssubclient.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/user_actions_client.useractionssubclient.md @@ -28,4 +28,4 @@ Retrieves all user actions for a particular case. **Returns:** *Promise*<[*ICaseUserActionsResponse*](typedoc_interfaces.icaseuseractionsresponse.md)\> -Defined in: [user_actions/client.ts:33](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/user_actions/client.ts#L33) +Defined in: [user_actions/client.ts:33](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/user_actions/client.ts#L33) diff --git a/x-pack/plugins/cases/docs/cases_client/modules/cases_get.md b/x-pack/plugins/cases/docs/cases_client/modules/cases_get.md index d4ca13501294a..4c165866cec47 100644 --- a/x-pack/plugins/cases/docs/cases_client/modules/cases_get.md +++ b/x-pack/plugins/cases/docs/cases_client/modules/cases_get.md @@ -31,7 +31,7 @@ Retrieves the reporters from all the cases. **Returns:** *Promise* -Defined in: [cases/get.ts:290](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/get.ts#L290) +Defined in: [cases/get.ts:289](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/get.ts#L289) ___ @@ -50,4 +50,4 @@ Retrieves the tags from all the cases. **Returns:** *Promise* -Defined in: [cases/get.ts:240](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/get.ts#L240) +Defined in: [cases/get.ts:239](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/get.ts#L239) diff --git a/x-pack/plugins/cases/public/components/__mock__/form.ts b/x-pack/plugins/cases/public/components/__mock__/form.ts index 6d3e8353e630a..aa40ea0421b4c 100644 --- a/x-pack/plugins/cases/public/components/__mock__/form.ts +++ b/x-pack/plugins/cases/public/components/__mock__/form.ts @@ -23,6 +23,7 @@ export const mockFormHook = { setFieldErrors: jest.fn(), getFields: jest.fn(), getFormData: jest.fn(), + getFieldDefaultValue: jest.fn(), /* Returns a list of all errors in the form */ getErrors: jest.fn(), reset: jest.fn(), @@ -33,7 +34,6 @@ export const mockFormHook = { __validateFields: jest.fn(), __updateFormDataAt: jest.fn(), __readFieldConfigFromSchema: jest.fn(), - __getFieldDefaultValue: jest.fn(), }; export const getFormMock = (sampleData: any) => ({ diff --git a/x-pack/plugins/console_extensions/README.md b/x-pack/plugins/console_extensions/README.md deleted file mode 100644 index 49d83d2888d6b..0000000000000 --- a/x-pack/plugins/console_extensions/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Console extensions - -This plugin provides autocomplete definitions of licensed APIs to the OSS Console plugin. \ No newline at end of file diff --git a/x-pack/plugins/console_extensions/kibana.json b/x-pack/plugins/console_extensions/kibana.json deleted file mode 100644 index 9411523d3f6dd..0000000000000 --- a/x-pack/plugins/console_extensions/kibana.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "id": "consoleExtensions", - "version": "1.0.0", - "kibanaVersion": "kibana", - "requiredPlugins": ["console"], - "server": true, - "ui": false -} diff --git a/x-pack/plugins/console_extensions/server/index.ts b/x-pack/plugins/console_extensions/server/index.ts deleted file mode 100644 index a03111a487090..0000000000000 --- a/x-pack/plugins/console_extensions/server/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { PluginInitializerContext, PluginConfigDescriptor } from 'kibana/server'; - -import { config as configSchema, ConfigType } from './config'; -import { ConsoleExtensionsServerPlugin } from './plugin'; - -export const plugin = (ctx: PluginInitializerContext) => new ConsoleExtensionsServerPlugin(ctx); - -export const config: PluginConfigDescriptor = { - schema: configSchema, -}; diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/js/ingest.ts b/x-pack/plugins/console_extensions/server/lib/spec_definitions/js/ingest.ts deleted file mode 100644 index 36ebfa589b823..0000000000000 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/js/ingest.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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -// NOTE: This is copy-pasted from es_6_0/ingest.js in OSS Console. -const commonPipelineParams = { - on_failure: [], - ignore_failure: { - __one_of: [false, true], - }, - if: '', - tag: '', -}; - -// Based on https://www.elastic.co/guide/en/elasticsearch/reference/master/enrich-processor.html -const enrichProcessorDefinition = { - enrich: { - __template: { - policy_name: '', - field: '', - target_field: '', - }, - policy_name: '', - field: '', - target_field: '', - ignore_missing: { - __one_of: [false, true], - }, - override: { - __one_of: [true, false], - }, - max_matches: 1, - shape_relation: 'INTERSECTS', - ...commonPipelineParams, - }, -}; - -// Based on https://www.elastic.co/guide/en/elasticsearch/reference/master/inference-processor.html -const inferenceProcessorDefinition = { - inference: { - __template: { - model_id: '', - inference_config: {}, - field_mappings: {}, - }, - target_field: '', - model_id: '', - field_mappings: { - __template: {}, - }, - inference_config: { - regression: { - __template: {}, - results_field: '', - }, - classification: { - __template: {}, - results_field: '', - num_top_classes: 2, - top_classes_results_field: '', - }, - }, - ...commonPipelineParams, - }, -}; - -export const processors = [enrichProcessorDefinition, inferenceProcessorDefinition]; diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_expired_data.json b/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_expired_data.json deleted file mode 100644 index 4afa9e323b030..0000000000000 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_expired_data.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "ml.delete_expired_data": { - "methods": [ - "DELETE" - ], - "patterns": [ - "_ml/_delete_expired_data" - ] - } -} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.info.json b/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.info.json deleted file mode 100644 index 51b571776ead9..0000000000000 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.info.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "ml.info": { - "methods": [ - "GET" - ], - "patterns": [ - "_ml/info" - ] - } -} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.put_filter.json b/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.put_filter.json deleted file mode 100644 index 6d57c433d71f4..0000000000000 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.put_filter.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "ml.put_filter": { - "methods": [ - "PUT" - ], - "patterns": [ - "_ml/filters/{filter_id}" - ] - } -} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.put_trained_model.json b/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.put_trained_model.json deleted file mode 100644 index 27d0393be6086..0000000000000 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.put_trained_model.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "ml.put_trained_model": { - "methods": [ - "PUT" - ], - "patterns": [ - "_ml/inference/{model_id}" - ], - "documentation": "TODO" - } -} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.delete_job.json b/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.delete_job.json deleted file mode 100644 index 8ecf617751a51..0000000000000 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.delete_job.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "rollup.delete_job": { - "methods": [ - "DELETE" - ], - "patterns": [ - "_rollup/job/{id}" - ] - } -} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.put_job.json b/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.put_job.json deleted file mode 100644 index 7734fd54a1ab1..0000000000000 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.put_job.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "rollup.put_job": { - "methods": [ - "PUT" - ], - "patterns": [ - "_rollup/job/{id}" - ] - } -} diff --git a/x-pack/plugins/console_extensions/server/plugin.ts b/x-pack/plugins/console_extensions/server/plugin.ts deleted file mode 100644 index 9ea3f314296ee..0000000000000 --- a/x-pack/plugins/console_extensions/server/plugin.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { join } from 'path'; -import { CoreSetup, CoreStart, Logger, Plugin, PluginInitializerContext } from 'kibana/server'; - -import { ConsoleSetup, ConsoleStart } from '../../../../src/plugins/console/server'; - -import { processors } from './lib/spec_definitions/js'; - -interface SetupDependencies { - console: ConsoleSetup; -} - -interface StartDependencies { - console: ConsoleStart; -} - -const CONSOLE_XPACK_JSON_SPEC_PATH = join(__dirname, 'lib/spec_definitions/json'); - -export class ConsoleExtensionsServerPlugin implements Plugin { - log: Logger; - constructor(private readonly ctx: PluginInitializerContext) { - this.log = this.ctx.logger.get(); - } - - setup(core: CoreSetup, { console: { addExtensionSpecFilePath } }: SetupDependencies) { - addExtensionSpecFilePath(CONSOLE_XPACK_JSON_SPEC_PATH); - this.log.debug(`Added extension path to ${CONSOLE_XPACK_JSON_SPEC_PATH}...`); - } - - start(core: CoreStart, { console: { addProcessorDefinition } }: StartDependencies) { - processors.forEach((processor) => addProcessorDefinition(processor)); - this.log.debug('Added processor definition extensions.'); - } -} diff --git a/x-pack/plugins/console_extensions/tsconfig.json b/x-pack/plugins/console_extensions/tsconfig.json deleted file mode 100644 index 5ad28f230a0bb..0000000000000 --- a/x-pack/plugins/console_extensions/tsconfig.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "composite": true, - "outDir": "./target/types", - "emitDeclarationOnly": true, - "declaration": true, - "declarationMap": true - }, - "include": [ - "server/**/*" - ], - "references": [ - { "path": "../../../src/core/tsconfig.json" }, - { "path": "../../../src/plugins/console/tsconfig.json" } - ] -} diff --git a/x-pack/plugins/data_enhanced/server/search/session/check_non_persiseted_sessions.ts b/x-pack/plugins/data_enhanced/server/search/session/check_non_persiseted_sessions.ts index 8c75ce91cac6a..2115ce85eeb27 100644 --- a/x-pack/plugins/data_enhanced/server/search/session/check_non_persiseted_sessions.ts +++ b/x-pack/plugins/data_enhanced/server/search/session/check_non_persiseted_sessions.ts @@ -59,7 +59,11 @@ function checkNonPersistedSessionsPage( `${SEARCH_SESSIONS_CLEANUP_TASK_TYPE} Found ${nonPersistedSearchSessions.total} sessions, processing ${nonPersistedSearchSessions.saved_objects.length}` ); - const updatedSessions = await getAllSessionsStatusUpdates(deps, nonPersistedSearchSessions); + const updatedSessions = await getAllSessionsStatusUpdates( + deps, + config, + nonPersistedSearchSessions + ); const deletedSessionIds: string[] = []; await Promise.all( diff --git a/x-pack/plugins/data_enhanced/server/search/session/check_persisted_sessions.ts b/x-pack/plugins/data_enhanced/server/search/session/check_persisted_sessions.ts index 0d51e97952275..3e89383c16d5e 100644 --- a/x-pack/plugins/data_enhanced/server/search/session/check_persisted_sessions.ts +++ b/x-pack/plugins/data_enhanced/server/search/session/check_persisted_sessions.ts @@ -36,7 +36,11 @@ function checkPersistedSessionsPage( `${SEARCH_SESSIONS_TASK_TYPE} Found ${persistedSearchSessions.total} sessions, processing ${persistedSearchSessions.saved_objects.length}` ); - const updatedSessions = await getAllSessionsStatusUpdates(deps, persistedSearchSessions); + const updatedSessions = await getAllSessionsStatusUpdates( + deps, + config, + persistedSearchSessions + ); await bulkUpdateSessions(deps, updatedSessions); return persistedSearchSessions; diff --git a/x-pack/plugins/data_enhanced/server/search/session/expire_persisted_sessions.ts b/x-pack/plugins/data_enhanced/server/search/session/expire_persisted_sessions.ts index e261c324f440f..61d1635dabe1b 100644 --- a/x-pack/plugins/data_enhanced/server/search/session/expire_persisted_sessions.ts +++ b/x-pack/plugins/data_enhanced/server/search/session/expire_persisted_sessions.ts @@ -36,7 +36,7 @@ function checkSessionExpirationPage( `${SEARCH_SESSIONS_EXPIRE_TASK_TYPE} Found ${searchSessions.total} sessions, processing ${searchSessions.saved_objects.length}` ); - const updatedSessions = await getAllSessionsStatusUpdates(deps, searchSessions); + const updatedSessions = await getAllSessionsStatusUpdates(deps, config, searchSessions); await bulkUpdateSessions(deps, updatedSessions); return searchSessions; diff --git a/x-pack/plugins/data_enhanced/server/search/session/get_session_status.test.ts b/x-pack/plugins/data_enhanced/server/search/session/get_session_status.test.ts index fc86e75297393..c3946e5af16fa 100644 --- a/x-pack/plugins/data_enhanced/server/search/session/get_session_status.test.ts +++ b/x-pack/plugins/data_enhanced/server/search/session/get_session_status.test.ts @@ -5,16 +5,21 @@ * 2.0. */ -import { SearchStatus } from './types'; +import { SearchSessionsConfig, SearchStatus } from './types'; import { getSessionStatus } from './get_session_status'; import { SearchSessionStatus } from '../../../../../../src/plugins/data/common'; +import moment from 'moment'; describe('getSessionStatus', () => { + const mockConfig = ({ + notTouchedInProgressTimeout: moment.duration(1, 'm'), + } as unknown) as SearchSessionsConfig; test("returns an in_progress status if there's nothing inside the session", () => { const session: any = { idMapping: {}, + touched: moment(), }; - expect(getSessionStatus(session)).toBe(SearchSessionStatus.IN_PROGRESS); + expect(getSessionStatus(session, mockConfig)).toBe(SearchSessionStatus.IN_PROGRESS); }); test("returns an error status if there's at least one error", () => { @@ -25,7 +30,25 @@ describe('getSessionStatus', () => { c: { status: SearchStatus.COMPLETE }, }, }; - expect(getSessionStatus(session)).toBe(SearchSessionStatus.ERROR); + expect(getSessionStatus(session, mockConfig)).toBe(SearchSessionStatus.ERROR); + }); + + test('expires a empty session after a minute', () => { + const session: any = { + idMapping: {}, + touched: moment().subtract(2, 'm'), + }; + expect(getSessionStatus(session, mockConfig)).toBe(SearchSessionStatus.EXPIRED); + }); + + test('doesnt expire a full session after a minute', () => { + const session: any = { + idMapping: { + a: { status: SearchStatus.IN_PROGRESS }, + }, + touched: moment().subtract(2, 'm'), + }; + expect(getSessionStatus(session, mockConfig)).toBe(SearchSessionStatus.IN_PROGRESS); }); test('returns a complete status if all are complete', () => { @@ -36,7 +59,7 @@ describe('getSessionStatus', () => { c: { status: SearchStatus.COMPLETE }, }, }; - expect(getSessionStatus(session)).toBe(SearchSessionStatus.COMPLETE); + expect(getSessionStatus(session, mockConfig)).toBe(SearchSessionStatus.COMPLETE); }); test('returns a running status if some are still running', () => { @@ -47,6 +70,6 @@ describe('getSessionStatus', () => { c: { status: SearchStatus.IN_PROGRESS }, }, }; - expect(getSessionStatus(session)).toBe(SearchSessionStatus.IN_PROGRESS); + expect(getSessionStatus(session, mockConfig)).toBe(SearchSessionStatus.IN_PROGRESS); }); }); diff --git a/x-pack/plugins/data_enhanced/server/search/session/get_session_status.ts b/x-pack/plugins/data_enhanced/server/search/session/get_session_status.ts index 23e02eedc0004..e7ae52b6c88ae 100644 --- a/x-pack/plugins/data_enhanced/server/search/session/get_session_status.ts +++ b/x-pack/plugins/data_enhanced/server/search/session/get_session_status.ts @@ -5,16 +5,28 @@ * 2.0. */ +import moment from 'moment'; import { SearchSessionSavedObjectAttributes, SearchSessionStatus, } from '../../../../../../src/plugins/data/common/'; -import { SearchStatus } from './types'; +import { SearchSessionsConfig, SearchStatus } from './types'; -export function getSessionStatus(session: SearchSessionSavedObjectAttributes): SearchSessionStatus { +export function getSessionStatus( + session: SearchSessionSavedObjectAttributes, + config: SearchSessionsConfig +): SearchSessionStatus { const searchStatuses = Object.values(session.idMapping); + const curTime = moment(); if (searchStatuses.some((item) => item.status === SearchStatus.ERROR)) { return SearchSessionStatus.ERROR; + } else if ( + searchStatuses.length === 0 && + curTime.diff(moment(session.touched), 'ms') > + moment.duration(config.notTouchedInProgressTimeout).asMilliseconds() + ) { + // Expire empty sessions that weren't touched for a minute + return SearchSessionStatus.EXPIRED; } else if ( searchStatuses.length > 0 && searchStatuses.every((item) => item.status === SearchStatus.COMPLETE) diff --git a/x-pack/plugins/data_enhanced/server/search/session/update_session_status.test.ts b/x-pack/plugins/data_enhanced/server/search/session/update_session_status.test.ts index 485a30fd54951..d9e3fa6f8cab3 100644 --- a/x-pack/plugins/data_enhanced/server/search/session/update_session_status.test.ts +++ b/x-pack/plugins/data_enhanced/server/search/session/update_session_status.test.ts @@ -21,6 +21,7 @@ import { describe('bulkUpdateSessions', () => { let mockClient: any; + const mockConfig: any = {}; let savedObjectsClient: jest.Mocked; const mockLogger: any = { debug: jest.fn(), @@ -66,6 +67,7 @@ describe('bulkUpdateSessions', () => { client: mockClient, logger: mockLogger, }, + mockConfig, so ); @@ -105,6 +107,7 @@ describe('bulkUpdateSessions', () => { client: mockClient, logger: mockLogger, }, + mockConfig, so ); @@ -139,6 +142,7 @@ describe('bulkUpdateSessions', () => { client: mockClient, logger: mockLogger, }, + mockConfig, so ); @@ -176,6 +180,7 @@ describe('bulkUpdateSessions', () => { client: mockClient, logger: mockLogger, }, + mockConfig, so ); @@ -219,6 +224,7 @@ describe('bulkUpdateSessions', () => { client: mockClient, logger: mockLogger, }, + mockConfig, so ); diff --git a/x-pack/plugins/data_enhanced/server/search/session/update_session_status.ts b/x-pack/plugins/data_enhanced/server/search/session/update_session_status.ts index 1c484467bef63..4758e7cb22684 100644 --- a/x-pack/plugins/data_enhanced/server/search/session/update_session_status.ts +++ b/x-pack/plugins/data_enhanced/server/search/session/update_session_status.ts @@ -13,11 +13,17 @@ import { } from '../../../../../../src/plugins/data/common'; import { getSearchStatus } from './get_search_status'; import { getSessionStatus } from './get_session_status'; -import { CheckSearchSessionsDeps, SearchSessionsResponse, SearchStatus } from './types'; +import { + CheckSearchSessionsDeps, + SearchSessionsConfig, + SearchSessionsResponse, + SearchStatus, +} from './types'; import { isSearchSessionExpired } from './utils'; export async function updateSessionStatus( { logger, client }: CheckSearchSessionsDeps, + config: SearchSessionsConfig, session: SavedObjectsFindResult ) { let sessionUpdated = false; @@ -61,7 +67,7 @@ export async function updateSessionStatus( // And only then derive the session's status const sessionStatus = isExpired ? SearchSessionStatus.EXPIRED - : getSessionStatus(session.attributes); + : getSessionStatus(session.attributes, config); if (sessionStatus !== session.attributes.status) { const now = new Date().toISOString(); session.attributes.status = sessionStatus; @@ -79,13 +85,14 @@ export async function updateSessionStatus( export async function getAllSessionsStatusUpdates( deps: CheckSearchSessionsDeps, + config: SearchSessionsConfig, searchSessions: SearchSessionsResponse ) { const updatedSessions = new Array>(); await Promise.all( searchSessions.saved_objects.map(async (session) => { - const updated = await updateSessionStatus(deps, session); + const updated = await updateSessionStatus(deps, config, session); if (updated) { updatedSessions.push(session); diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_view.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_view.tsx index ec55eddec2a79..f45c4a89d006c 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_view.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_view.tsx @@ -68,6 +68,7 @@ import { TimeBuckets } from '../../services/time_buckets'; import { extractSearchData } from '../../utils/saved_search_utils'; import { DataVisualizerIndexPatternManagement } from '../index_pattern_management'; import { ResultLink } from '../../../common/components/results_links'; +import { extractErrorProperties } from '../../utils/error_utils'; interface DataVisualizerPageState { overallStats: OverallStats; @@ -371,9 +372,16 @@ export const IndexDataVisualizerView: FC = (dataVi earliest, latest ); + // Because load overall stats perform queries in batches + // there could be multiple errors + if (Array.isArray(allStats.errors) && allStats.errors.length > 0) { + allStats.errors.forEach((err: any) => { + dataLoader.displayError(extractErrorProperties(err)); + }); + } setOverallStats(allStats); } catch (err) { - dataLoader.displayError(err); + dataLoader.displayError(err.body ?? err); } } diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/data_loader/data_loader.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/data_loader/data_loader.ts index 4a3c971cc57cd..1b92eaddd1343 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/data_loader/data_loader.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/data_loader/data_loader.ts @@ -109,17 +109,17 @@ export class DataLoader { 'The request may have timed out. Try using a smaller sample size or narrowing the time range.', values: { index: this._indexPattern.title, - message: err.message, + message: err.error ?? err.message, }, }), }); } else { this._toastNotifications.addError(err, { - title: i18n.translate('xpack.dataVisualizer.index.errorLoadingDataMessage.', { - defaultMessage: 'Error loading data in index {index}. {message}', + title: i18n.translate('xpack.dataVisualizer.index.errorLoadingDataMessage', { + defaultMessage: 'Error loading data in index {index}. {message}.', values: { index: this._indexPattern.title, - message: err.message, + message: err.error ?? err.message, }, }), }); diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/error_utils.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/error_utils.ts new file mode 100644 index 0000000000000..9bb36496a149e --- /dev/null +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/error_utils.ts @@ -0,0 +1,184 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { HttpFetchError } from 'kibana/public'; +import Boom from '@hapi/boom'; +import { isPopulatedObject } from '../../../../common/utils/object_utils'; + +export interface WrappedError { + body: { + attributes: { + body: EsErrorBody; + }; + message: Boom.Boom; + }; + statusCode: number; +} + +export interface EsErrorRootCause { + type: string; + reason: string; + caused_by?: EsErrorRootCause; + script?: string; +} + +export interface EsErrorBody { + error: { + root_cause?: EsErrorRootCause[]; + caused_by?: EsErrorRootCause; + type: string; + reason: string; + }; + status: number; +} + +export interface DVResponseError { + statusCode: number; + error: string; + message: string; + attributes?: { + body: EsErrorBody; + }; +} + +export interface ErrorMessage { + message: string; +} + +export interface DVErrorObject { + causedBy?: string; + message: string; + statusCode?: number; + fullError?: EsErrorBody; +} + +export interface DVHttpFetchError extends HttpFetchError { + body: T; +} + +export type ErrorType = + | WrappedError + | DVHttpFetchError + | EsErrorBody + | Boom.Boom + | string + | undefined; + +export function isEsErrorBody(error: any): error is EsErrorBody { + return error && error.error?.reason !== undefined; +} + +export function isErrorString(error: any): error is string { + return typeof error === 'string'; +} + +export function isErrorMessage(error: any): error is ErrorMessage { + return error && error.message !== undefined && typeof error.message === 'string'; +} + +export function isDVResponseError(error: any): error is DVResponseError { + return typeof error.body === 'object' && 'message' in error.body; +} + +export function isBoomError(error: any): error is Boom.Boom { + return error.isBoom === true; +} + +export function isWrappedError(error: any): error is WrappedError { + return error && isBoomError(error.body?.message) === true; +} + +export const extractErrorProperties = (error: ErrorType): DVErrorObject => { + // extract properties of the error object from within the response error + // coming from Kibana, Elasticsearch, and our own DV messages + + // some responses contain raw es errors as part of a bulk response + // e.g. if some jobs fail the action in a bulk request + + if (isEsErrorBody(error)) { + return { + message: error.error.reason, + statusCode: error.status, + fullError: error, + }; + } + + if (isErrorString(error)) { + return { + message: error, + }; + } + if (isWrappedError(error)) { + return error.body.message?.output?.payload; + } + + if (isBoomError(error)) { + return { + message: error.output.payload.message, + statusCode: error.output.payload.statusCode, + }; + } + + if (error?.body === undefined && !error?.message) { + return { + message: '', + }; + } + + if (typeof error.body === 'string') { + return { + message: error.body, + }; + } + + if (isDVResponseError(error)) { + if ( + typeof error.body.attributes === 'object' && + typeof error.body.attributes.body?.error?.reason === 'string' + ) { + const errObj: DVErrorObject = { + message: error.body.attributes.body.error.reason, + statusCode: error.body.statusCode, + fullError: error.body.attributes.body, + }; + if ( + typeof error.body.attributes.body.error.caused_by === 'object' && + (typeof error.body.attributes.body.error.caused_by?.reason === 'string' || + typeof error.body.attributes.body.error.caused_by?.caused_by?.reason === 'string') + ) { + errObj.causedBy = + error.body.attributes.body.error.caused_by?.caused_by?.reason || + error.body.attributes.body.error.caused_by?.reason; + } + if ( + Array.isArray(error.body.attributes.body.error.root_cause) && + typeof error.body.attributes.body.error.root_cause[0] === 'object' && + isPopulatedObject(error.body.attributes.body.error.root_cause[0], ['script']) + ) { + errObj.causedBy = error.body.attributes.body.error.root_cause[0].script; + errObj.message += `: '${error.body.attributes.body.error.root_cause[0].script}'`; + } + return errObj; + } else { + return { + message: error.body.message, + statusCode: error.body.statusCode, + }; + } + } + + if (isErrorMessage(error)) { + return { + message: error.message, + }; + } + + // If all else fail return an empty message instead of JSON.stringify + return { + message: '', + }; +}; diff --git a/x-pack/plugins/data_visualizer/server/models/data_visualizer/data_visualizer.ts b/x-pack/plugins/data_visualizer/server/models/data_visualizer/data_visualizer.ts index 27c09c889deb7..155cf09ebb8db 100644 --- a/x-pack/plugins/data_visualizer/server/models/data_visualizer/data_visualizer.ts +++ b/x-pack/plugins/data_visualizer/server/models/data_visualizer/data_visualizer.ts @@ -31,6 +31,7 @@ import { getNumericFieldsStats, getStringFieldsStats, } from './get_fields_stats'; +import { wrapError } from '../../utils/error_wrapper'; export class DataVisualizer { private _client: IScopedClusterClient; @@ -60,6 +61,7 @@ export class DataVisualizer { aggregatableNotExistsFields: [] as FieldData[], nonAggregatableExistsFields: [] as FieldData[], nonAggregatableNotExistsFields: [] as FieldData[], + errors: [] as any[], }; // To avoid checking for the existence of too many aggregatable fields in one request, @@ -76,49 +78,61 @@ export class DataVisualizer { await Promise.all( batches.map(async (fields) => { - const batchStats = await this.checkAggregatableFieldsExist( - indexPatternTitle, - query, - fields, - samplerShardSize, - timeFieldName, - earliestMs, - latestMs, - undefined, - runtimeMappings - ); + try { + const batchStats = await this.checkAggregatableFieldsExist( + indexPatternTitle, + query, + fields, + samplerShardSize, + timeFieldName, + earliestMs, + latestMs, + undefined, + runtimeMappings + ); - // Total count will be returned with each batch of fields. Just overwrite. - stats.totalCount = batchStats.totalCount; + // Total count will be returned with each batch of fields. Just overwrite. + stats.totalCount = batchStats.totalCount; - // Add to the lists of fields which do and do not exist. - stats.aggregatableExistsFields.push(...batchStats.aggregatableExistsFields); - stats.aggregatableNotExistsFields.push(...batchStats.aggregatableNotExistsFields); + // Add to the lists of fields which do and do not exist. + stats.aggregatableExistsFields.push(...batchStats.aggregatableExistsFields); + stats.aggregatableNotExistsFields.push(...batchStats.aggregatableNotExistsFields); + } catch (e) { + // If index not found, no need to proceed with other batches + if (e.statusCode === 404) { + throw e; + } + stats.errors.push(wrapError(e)); + } }) ); await Promise.all( nonAggregatableFields.map(async (field) => { - const existsInDocs = await this.checkNonAggregatableFieldExists( - indexPatternTitle, - query, - field, - timeFieldName, - earliestMs, - latestMs, - runtimeMappings - ); + try { + const existsInDocs = await this.checkNonAggregatableFieldExists( + indexPatternTitle, + query, + field, + timeFieldName, + earliestMs, + latestMs, + runtimeMappings + ); - const fieldData: FieldData = { - fieldName: field, - existsInDocs, - stats: {}, - }; + const fieldData: FieldData = { + fieldName: field, + existsInDocs, + stats: {}, + }; - if (existsInDocs === true) { - stats.nonAggregatableExistsFields.push(fieldData); - } else { - stats.nonAggregatableNotExistsFields.push(fieldData); + if (existsInDocs === true) { + stats.nonAggregatableExistsFields.push(fieldData); + } else { + stats.nonAggregatableNotExistsFields.push(fieldData); + } + } catch (e) { + stats.errors.push(wrapError(e)); } }) ); diff --git a/x-pack/plugins/data_visualizer/server/plugin.ts b/x-pack/plugins/data_visualizer/server/plugin.ts index 4ae695b05b81f..9db580959b116 100644 --- a/x-pack/plugins/data_visualizer/server/plugin.ts +++ b/x-pack/plugins/data_visualizer/server/plugin.ts @@ -12,7 +12,7 @@ import { dataVisualizerRoutes } from './routes'; export class DataVisualizerPlugin implements Plugin { constructor() {} - async setup(coreSetup: CoreSetup, plugins: SetupDeps) { + setup(coreSetup: CoreSetup, plugins: SetupDeps) { dataVisualizerRoutes(coreSetup); } 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 619d9d07bf5f3..765c8c5ea847a 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 @@ -32,17 +32,23 @@ export const CredentialsList: React.FC = () => { const columns: Array> = [ { - name: 'Name', + name: i18n.translate('xpack.enterpriseSearch.appSearch.credentials.list.nameTitle', { + defaultMessage: 'Name', + }), width: '12%', render: (token: ApiToken) => token.name, }, { - name: 'Type', + name: i18n.translate('xpack.enterpriseSearch.appSearch.credentials.list.typeTitle', { + defaultMessage: 'Type', + }), width: '15%', render: (token: ApiToken) => TOKEN_TYPE_DISPLAY_NAMES[token.type], }, { - name: 'Key', + name: i18n.translate('xpack.enterpriseSearch.appSearch.credentials.list.keyTitle', { + defaultMessage: 'Key', + }), width: '36%', className: 'eui-textBreakAll', render: (token: ApiToken) => { @@ -71,12 +77,16 @@ export const CredentialsList: React.FC = () => { }, }, { - name: 'Modes', + name: i18n.translate('xpack.enterpriseSearch.appSearch.credentials.list.modesTitle', { + defaultMessage: 'Modes', + }), width: '10%', render: (token: ApiToken) => getModeDisplayText(token), }, { - name: 'Engines', + name: i18n.translate('xpack.enterpriseSearch.appSearch.credentials.list.enginesTitle', { + defaultMessage: 'Engines', + }), width: '18%', render: (token: ApiToken) => getEnginesDisplayText(token), }, diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/components/search_ui_form.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/components/search_ui_form.test.tsx index 944c99f7c61c2..332f3a3889a32 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/components/search_ui_form.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/components/search_ui_form.test.tsx @@ -39,6 +39,7 @@ describe('SearchUIForm', () => { onSortFieldsChange: jest.fn(), onTitleFieldChange: jest.fn(), onUrlFieldChange: jest.fn(), + onThumbnailFieldChange: jest.fn(), }; beforeAll(() => { @@ -52,6 +53,7 @@ describe('SearchUIForm', () => { expect(wrapper.find('[data-test-subj="selectFilters"]').exists()).toBe(true); expect(wrapper.find('[data-test-subj="selectSort"]').exists()).toBe(true); expect(wrapper.find('[data-test-subj="selectUrl"]').exists()).toBe(true); + expect(wrapper.find('[data-test-subj="selectThumbnail"]').exists()).toBe(true); }); describe('title field', () => { @@ -112,6 +114,35 @@ describe('SearchUIForm', () => { }); }); + describe('thumbnail field', () => { + beforeEach(() => jest.clearAllMocks()); + const subject = () => shallow().find('[data-test-subj="selectThumbnail"]'); + + it('renders with its value set from state', () => { + setMockValues({ + ...values, + thumbnailField: 'foo', + }); + + expect(subject().prop('value')).toBe('foo'); + }); + + it('updates state with new value when changed', () => { + subject().simulate('change', { target: { value: 'foo' } }); + expect(actions.onThumbnailFieldChange).toHaveBeenCalledWith('foo'); + }); + + it('updates active field in state on focus', () => { + subject().simulate('focus'); + expect(actions.onActiveFieldChange).toHaveBeenCalledWith(ActiveField.Thumb); + }); + + it('removes active field in state on blur', () => { + subject().simulate('blur'); + expect(actions.onActiveFieldChange).toHaveBeenCalledWith(ActiveField.None); + }); + }); + describe('filters field', () => { beforeEach(() => jest.clearAllMocks()); const subject = () => shallow().find('[data-test-subj="selectFilters"]'); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/components/search_ui_form.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/components/search_ui_form.tsx index b795a46268237..145362812a7bf 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/components/search_ui_form.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/components/search_ui_form.tsx @@ -22,6 +22,8 @@ import { URL_FIELD_LABEL, URL_FIELD_HELP_TEXT, GENERATE_PREVIEW_BUTTON_LABEL, + THUMBNAIL_FIELD_LABEL, + THUMBNAIL_FIELD_HELP_TEXT, } from '../i18n'; import { SearchUILogic } from '../search_ui_logic'; import { ActiveField } from '../types'; @@ -36,6 +38,7 @@ export const SearchUIForm: React.FC = () => { validFacetFields, titleField, urlField, + thumbnailField, facetFields, sortFields, } = useValues(SearchUILogic); @@ -45,11 +48,13 @@ export const SearchUIForm: React.FC = () => { onSortFieldsChange, onTitleFieldChange, onUrlFieldChange, + onThumbnailFieldChange, } = useActions(SearchUILogic); const previewHref = generatePreviewUrl({ titleField, urlField, + thumbnailField, facets: facetFields, sortFields, }); @@ -69,6 +74,7 @@ export const SearchUIForm: React.FC = () => { const facetOptionFields = formatMultiOptions(validFacetFields); const selectedTitleOption = formatSelectOption(titleField); const selectedURLOption = formatSelectOption(urlField); + const selectedThumbnailOption = formatSelectOption(thumbnailField); const selectedSortOptions = formatMultiOptions(sortFields); const selectedFacetOptions = formatMultiOptions(facetFields); @@ -112,7 +118,6 @@ export const SearchUIForm: React.FC = () => { data-test-subj="selectSort" /> - { data-test-subj="selectUrl" /> + + onThumbnailFieldChange(e.target.value)} + fullWidth + onFocus={() => onActiveFieldChange(ActiveField.Thumb)} + onBlur={() => onActiveFieldChange(ActiveField.None)} + hasNoInitialSelection + data-test-subj="selectThumbnail" + /> + { validFacetFields: [], titleField: '', urlField: '', + thumbnailField: '', facetFields: [], sortFields: [], activeField: ActiveField.None, @@ -93,6 +94,17 @@ describe('SearchUILogic', () => { }); }); + describe('onThumbnailFieldChange', () => { + it('sets the thumbnailField value', () => { + mount({ thumbnailField: '' }); + SearchUILogic.actions.onThumbnailFieldChange('foo'); + expect(SearchUILogic.values).toEqual({ + ...DEFAULT_VALUES, + thumbnailField: 'foo', + }); + }); + }); + describe('onFacetFieldsChange', () => { it('sets the facetFields value', () => { mount({ facetFields: [] }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/search_ui_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/search_ui_logic.ts index 096365f57ea36..e6225614fdc18 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/search_ui_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/search_ui_logic.ts @@ -20,6 +20,7 @@ interface InitialFieldValues { validSortFields: string[]; validFacetFields: string[]; urlField?: string; + thumbnailField?: string; titleField?: string; } interface SearchUIActions { @@ -30,6 +31,7 @@ interface SearchUIActions { onSortFieldsChange(sortFields: string[]): { sortFields: string[] }; onTitleFieldChange(titleField: string): { titleField: string }; onUrlFieldChange(urlField: string): { urlField: string }; + onThumbnailFieldChange(thumbnailField: string): { thumbnailField: string }; } interface SearchUIValues { @@ -39,6 +41,7 @@ interface SearchUIValues { validFacetFields: string[]; titleField: string; urlField: string; + thumbnailField: string; facetFields: string[]; sortFields: string[]; activeField: ActiveField; @@ -54,6 +57,7 @@ export const SearchUILogic = kea> onSortFieldsChange: (sortFields) => ({ sortFields }), onTitleFieldChange: (titleField) => ({ titleField }), onUrlFieldChange: (urlField) => ({ urlField }), + onThumbnailFieldChange: (thumbnailField) => ({ thumbnailField }), }), reducers: () => ({ dataLoading: [ @@ -79,6 +83,12 @@ export const SearchUILogic = kea> onFieldDataLoaded: (_, { urlField }) => urlField || '', }, ], + thumbnailField: [ + '', + { + onThumbnailFieldChange: (_, { thumbnailField }) => thumbnailField, + }, + ], facetFields: [[], { onFacetFieldsChange: (_, { facetFields }) => facetFields }], sortFields: [[], { onSortFieldsChange: (_, { sortFields }) => sortFields }], activeField: [ActiveField.None, { onActiveFieldChange: (_, { activeField }) => activeField }], diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/types.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/types.ts index 224aeab05af35..e5b0f2ce2ccd7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/types.ts @@ -10,5 +10,6 @@ export enum ActiveField { Filter = 'Filter', Sort = 'Sort', Url = 'Url', + Thumb = 'Thumb', None = '', } diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/account_header/account_header.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/account_header/account_header.tsx index 1e6e8ec6c5e49..4c22a234e731c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/account_header/account_header.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/account_header/account_header.tsx @@ -68,11 +68,11 @@ export const AccountHeader: React.FC = () => { return ( - + {WORKPLACE_SEARCH_TITLE} - + {ACCOUNT_NAV.SOURCES} diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.test.ts index fcaa847c47f3e..950412e84f870 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.test.ts @@ -20,8 +20,14 @@ jest.mock('../../../../app_logic', () => ({ })); import { AppLogic } from '../../../../app_logic'; -import { ADD_GITHUB_PATH, SOURCES_PATH, getSourcesPath } from '../../../../routes'; +import { + ADD_GITHUB_PATH, + SOURCES_PATH, + PERSONAL_SOURCES_PATH, + getSourcesPath, +} from '../../../../routes'; import { CustomSource } from '../../../../types'; +import { PERSONAL_DASHBOARD_SOURCE_ERROR } from '../../constants'; import { SourcesLogic } from '../../sources_logic'; import { @@ -36,7 +42,7 @@ describe('AddSourceLogic', () => { const { mount } = new LogicMounter(AddSourceLogic); const { http } = mockHttpValues; const { navigateToUrl } = mockKibanaValues; - const { clearFlashMessages, flashAPIErrors } = mockFlashMessageHelpers; + const { clearFlashMessages, flashAPIErrors, setErrorMessage } = mockFlashMessageHelpers; const defaultValues = { addSourceCurrentStep: AddSourceSteps.ConfigIntroStep, @@ -283,6 +289,7 @@ describe('AddSourceLogic', () => { describe('saveSourceParams', () => { const params = { code: 'code123', + session_state: 'session_state123', state: '{"action":"create","context":"organization","service_type":"gmail","csrf_token":"token==","index_permissions":false}', }; @@ -300,7 +307,7 @@ describe('AddSourceLogic', () => { const setAddedSourceSpy = jest.spyOn(SourcesLogic.actions, 'setAddedSource'); const { serviceName, indexPermissions, serviceType } = response; http.get.mockReturnValue(Promise.resolve(response)); - AddSourceLogic.actions.saveSourceParams(queryString); + AddSourceLogic.actions.saveSourceParams(queryString, params, true); expect(http.get).toHaveBeenCalledWith('/api/workplace_search/sources/create', { query: { ...params, @@ -318,7 +325,7 @@ describe('AddSourceLogic', () => { const accountQueryString = '?state=%7B%22action%22:%22create%22,%22context%22:%22account%22,%22service_type%22:%22gmail%22,%22csrf_token%22:%22token%3D%3D%22,%22index_permissions%22:false%7D&code=code'; - AddSourceLogic.actions.saveSourceParams(accountQueryString); + AddSourceLogic.actions.saveSourceParams(accountQueryString, params, false); await nextTick(); @@ -339,7 +346,7 @@ describe('AddSourceLogic', () => { preContentSourceId, }) ); - AddSourceLogic.actions.saveSourceParams(queryString); + AddSourceLogic.actions.saveSourceParams(queryString, params, true); expect(http.get).toHaveBeenCalledWith('/api/workplace_search/sources/create', { query: { ...params, @@ -353,10 +360,36 @@ describe('AddSourceLogic', () => { expect(navigateToUrl).toHaveBeenCalledWith(`${ADD_GITHUB_PATH}/configure${queryString}`); }); + describe('Github error edge case', () => { + const GITHUB_ERROR = + 'The redirect_uri MUST match the registered callback URL for this application.'; + const errorParams = { ...params, error_description: GITHUB_ERROR }; + const getGithubQueryString = (context: 'organization' | 'account') => + `?error=redirect_uri_mismatch&error_description=The+redirect_uri+MUST+match+the+registered+callback+URL+for+this+application.&error_uri=https%3A%2F%2Fdocs.github.com%2Fapps%2Fmanaging-oauth-apps%2Ftroubleshooting-authorization-request-errors%2F%23redirect-uri-mismatch&state=%7B%22action%22%3A%22create%22%2C%22context%22%3A%22${context}%22%2C%22service_type%22%3A%22github%22%2C%22csrf_token%22%3A%22TOKEN%3D%3D%22%2C%22index_permissions%22%3Afalse%7D`; + + it('handles "organization" redirect and displays error', () => { + const githubQueryString = getGithubQueryString('organization'); + AddSourceLogic.actions.saveSourceParams(githubQueryString, errorParams, true); + + expect(navigateToUrl).toHaveBeenCalledWith('/'); + expect(setErrorMessage).toHaveBeenCalledWith(GITHUB_ERROR); + }); + + it('handles "account" redirect and displays error', () => { + const githubQueryString = getGithubQueryString('account'); + AddSourceLogic.actions.saveSourceParams(githubQueryString, errorParams, false); + + expect(navigateToUrl).toHaveBeenCalledWith(PERSONAL_SOURCES_PATH); + expect(setErrorMessage).toHaveBeenCalledWith( + PERSONAL_DASHBOARD_SOURCE_ERROR(GITHUB_ERROR) + ); + }); + }); + it('handles error', async () => { http.get.mockReturnValue(Promise.reject('this is an error')); - AddSourceLogic.actions.saveSourceParams(queryString); + AddSourceLogic.actions.saveSourceParams(queryString, params, true); await nextTick(); expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.ts index 0bd37aed81c32..a75e494aa2b1c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.ts @@ -16,14 +16,20 @@ import { flashAPIErrors, setSuccessMessage, clearFlashMessages, + setErrorMessage, } from '../../../../../shared/flash_messages'; import { HttpLogic } from '../../../../../shared/http'; import { KibanaLogic } from '../../../../../shared/kibana'; -import { parseQueryParams } from '../../../../../shared/query_params'; import { AppLogic } from '../../../../app_logic'; import { CUSTOM_SERVICE_TYPE, WORKPLACE_SEARCH_URL_PREFIX } from '../../../../constants'; -import { SOURCES_PATH, ADD_GITHUB_PATH, getSourcesPath } from '../../../../routes'; +import { + SOURCES_PATH, + ADD_GITHUB_PATH, + PERSONAL_SOURCES_PATH, + getSourcesPath, +} from '../../../../routes'; import { CustomSource } from '../../../../types'; +import { PERSONAL_DASHBOARD_SOURCE_ERROR } from '../../constants'; import { staticSourceData } from '../../source_data'; import { SourcesLogic } from '../../sources_logic'; @@ -50,6 +56,8 @@ export interface OauthParams { state: string; session_state: string; oauth_verifier?: string; + error?: string; + error_description?: string; } export interface AddSourceActions { @@ -86,7 +94,11 @@ export interface AddSourceActions { isUpdating: boolean, successCallback?: () => void ): { isUpdating: boolean; successCallback?(): void }; - saveSourceParams(search: Search): { search: Search }; + saveSourceParams( + search: Search, + params: OauthParams, + isOrganization: boolean + ): { search: Search; params: OauthParams; isOrganization: boolean }; getSourceConfigData(serviceType: string): { serviceType: string }; getSourceConnectData( serviceType: string, @@ -197,7 +209,11 @@ export const AddSourceLogic = kea ({ search }), + saveSourceParams: (search: Search, params: OauthParams, isOrganization: boolean) => ({ + search, + params, + isOrganization, + }), createContentSource: ( serviceType: string, successCallback: () => void, @@ -491,15 +507,28 @@ export const AddSourceLogic = kea { + saveSourceParams: async ({ search, params, isOrganization }) => { const { http } = HttpLogic.values; const { navigateToUrl } = KibanaLogic.values; const { setAddedSource } = SourcesLogic.actions; - const params = (parseQueryParams(search) as unknown) as OauthParams; const query = { ...params, kibana_host: kibanaHost }; const route = '/api/workplace_search/sources/create'; - const state = JSON.parse(params.state); - const isOrganization = state.context !== 'account'; + + /** + There is an extreme edge case where the user is trying to connect Github as source from ent-search, + after configuring it in Kibana. When this happens, Github redirects the user from ent-search to Kibana + with special error properties in the query params. In this case we need to redirect the user to the + app home page and display the error message, and not persist the other query params to the server. + */ + if (params.error_description) { + navigateToUrl(isOrganization ? '/' : PERSONAL_SOURCES_PATH); + setErrorMessage( + isOrganization + ? params.error_description + : PERSONAL_DASHBOARD_SOURCE_ERROR(params.error_description) + ); + return; + } try { const response = await http.get(route, { query }); @@ -514,7 +543,7 @@ export const AddSourceLogic = kea { }); it('renders', () => { - const search = '?name=foo&serviceType=custom&indexPermissions=false'; + const search = + '?code=1234&state=%7B%22action%22%3A%22create%22%2C%22context%22%3A%22account%22%2C%22service_type%22%3A%22github%22%2C%22csrf_token%22%3A%22TOKEN123%3D%3D%22%2C%22index_permissions%22%3Afalse%7D'; (useLocation as jest.Mock).mockImplementationOnce(() => ({ search })); const wrapper = shallow(); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_added.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_added.tsx index 5b93b7a426936..77b3931050483 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_added.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_added.tsx @@ -15,8 +15,9 @@ import { EuiPage, EuiPageBody } from '@elastic/eui'; import { KibanaLogic } from '../../../../shared/kibana'; import { Loading } from '../../../../shared/loading'; +import { parseQueryParams } from '../../../../shared/query_params'; -import { AddSourceLogic } from './add_source/add_source_logic'; +import { AddSourceLogic, OauthParams } from './add_source/add_source_logic'; /** * This component merely triggers catchs the redirect from the oauth application and initializes the saving @@ -25,14 +26,17 @@ import { AddSourceLogic } from './add_source/add_source_logic'; */ export const SourceAdded: React.FC = () => { const { search } = useLocation() as Location; + const params = (parseQueryParams(search) as unknown) as OauthParams; + const state = JSON.parse(params.state); + const isOrganization = state.context !== 'account'; const { setChromeIsVisible } = useValues(KibanaLogic); const { saveSourceParams } = useActions(AddSourceLogic); // We don't want the personal dashboard to flash the Kibana chrome, so we hide it. - setChromeIsVisible(false); + setChromeIsVisible(isOrganization); useEffect(() => { - saveSourceParams(search); + saveSourceParams(search, params, isOrganization); }, []); return ( diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/constants.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/constants.ts index 8e5123dbc7cce..d57afc6699c1a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/constants.ts @@ -470,3 +470,10 @@ export const PRIVATE_DASHBOARD_READ_ONLY_MODE_WARNING = i18n.translate( 'Workplace Search is currently available for search only, due to regular maintenance. Contact your system administrator for more information.', } ); + +export const PERSONAL_DASHBOARD_SOURCE_ERROR = (error: string) => + i18n.translate('xpack.enterpriseSearch.workplaceSearch.personalDashboardSourceError', { + defaultMessage: + 'Could not connect the source, reach out to your admin for help. Error message: {error}', + values: { error }, + }); diff --git a/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.ts b/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.ts index 5de4387f2c0d9..835ad84ef6853 100644 --- a/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.ts +++ b/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.ts @@ -135,7 +135,7 @@ export function registerAccountCreateSourceRoute({ login: schema.maybe(schema.string()), password: schema.maybe(schema.string()), organizations: schema.maybe(schema.arrayOf(schema.string())), - indexPermissions: schema.boolean(), + indexPermissions: schema.maybe(schema.boolean()), }), }, }, diff --git a/x-pack/plugins/file_upload/server/plugin.ts b/x-pack/plugins/file_upload/server/plugin.ts index c729afec92f94..bd5eebe372a75 100644 --- a/x-pack/plugins/file_upload/server/plugin.ts +++ b/x-pack/plugins/file_upload/server/plugin.ts @@ -21,7 +21,7 @@ export class FileUploadPlugin implements Plugin { this._logger = initializerContext.logger.get(); } - async setup(coreSetup: CoreSetup, plugins: SetupDeps) { + setup(coreSetup: CoreSetup, plugins: SetupDeps) { fileUploadRoutes(coreSetup, this._logger); setupCapabilities(coreSetup); diff --git a/x-pack/plugins/fleet/README.md b/x-pack/plugins/fleet/README.md index 1b18b72647537..ce8e95c83d6bd 100644 --- a/x-pack/plugins/fleet/README.md +++ b/x-pack/plugins/fleet/README.md @@ -48,6 +48,48 @@ This plugin follows the `common`, `server`, `public` structure from the [Archite Note: The plugin was previously named Ingest Manager it's possible that some variables are still named with that old plugin name. +### Running Fleet Server Locally in a Container + +It can be useful to run Fleet Server in a container on your local machine in order to free up your actual "bare metal" machine to run Elastic Agent for testing purposes. Otherwise, you'll only be able to a single instance of Elastic Agent dedicated to Fleet Server on your local machine, and this can make testing integrations and policies difficult. + +_The following is adapted from the Fleet Server [README](https://github.com/elastic/fleet-server#running-elastic-agent-with-fleet-server-in-container)_ + +1. Add the following configuration to your `kibana.dev.yml` + +```yml +server.host: 0.0.0.0 +``` + +2. Append the following option to the command you use to start Elasticsearch + +``` +-E http.host=0.0.0.0 +``` + +This command should look something like this: + +``` +yarn es snapshot --license trial -E xpack.security.authc.api_key.enabled=true -E path.data=/tmp/es-data -E http.host=0.0.0.0 +``` + +3. Run the Fleet Server Docker container. Make sure you include a `BASE-PATH` value if your local Kibana instance is using one. `YOUR-IP` should correspond to the IP address used by your Docker network to represent the host. For Windows and Mac machines, this should be `192.168.65.2`. If you're not sure what this IP should be, run the following to look it up: + +``` +docker run -it --rm alpine nslookup host.docker.internal +``` + +To run the Fleet Server Docker container: + +``` +docker run -e KIBANA_HOST=http://{YOUR-IP}:5601/{BASE-PATH} -e KIBANA_USERNAME=elastic -e KIBANA_PASSWORD=changeme -e ELASTICSEARCH_HOST=http://{YOUR-IP}:9200 -e ELASTICSEARCH_USERNAME=elastic -e ELASTICSEARCH_PASSWORD=changeme -e KIBANA_FLEET_SETUP=1 -e FLEET_SERVER_ENABLE=1 -e FLEET_SERVER_INSECURE_HTTP=1 -p 8220:8220 docker.elastic.co/beats/elastic-agent:{VERSION} +``` + +Ensure you provide the `-p 8220:8220` port mapping to map the Fleet Server container's port `8220` to your local machine's port `8220` in order for Fleet to communicate with Fleet Server. + +For the latest version, use `8.0.0-SNAPSHOT`. Otherwise, you can explore the available versions at https://www.docker.elastic.co/r/beats/elastic-agent. + +Once the Fleet Server container is running, you should be able to treat it as if it were a local process running on `http://localhost:8220` when configuring Fleet via the UI. You can then run `elastic-agent` on your local machine directly for testing purposes. + ### Tests #### API integration tests @@ -77,3 +119,4 @@ You need to have `docker` to run ingest manager api integration tests ``` FLEET_PACKAGE_REGISTRY_DOCKER_IMAGE='docker.elastic.co/package-registry/distribution:production' FLEET_PACKAGE_REGISTRY_PORT=12345 yarn test:ftr:runner ``` + diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_cloud_instructions.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_cloud_instructions.tsx index 3b9d297f37df2..88590ce3ce504 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_cloud_instructions.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_cloud_instructions.tsx @@ -8,7 +8,6 @@ import React, { useEffect } from 'react'; import { EuiButton, - EuiPanel, EuiLink, EuiEmptyPrompt, EuiFlexItem, @@ -39,62 +38,54 @@ export const CloudInstructions: React.FC<{ deploymentUrl: string }> = ({ deploym return ( <> - - - - - } - body={ + - - - ), - }} + id="xpack.fleet.fleetServerSetup.cloudSetupTitle" + defaultMessage="Enable APM & Fleet" /> - } - actions={ - <> - - - - - } - /> - + + } + body={ + + + + ), + }} + /> + } + actions={ + <> + + + + + } + /> diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions.tsx index 0fc3821d2e3f7..1d43f90b80def 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions.tsx @@ -10,7 +10,6 @@ import { EuiButton, EuiFlexGroup, EuiFlexItem, - EuiPanel, EuiSpacer, EuiText, EuiLink, @@ -737,9 +736,8 @@ export const OnPremInstructions: React.FC = () => { }, [notifications.toasts]); return ( - - - + <> +

{ : CompleteStep(), ]} /> - + ); }; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/es_requirements_page.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/es_requirements_page.tsx index b4e6f1007536f..9c5ec12645c1d 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/es_requirements_page.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/es_requirements_page.tsx @@ -72,7 +72,8 @@ export const MissingESRequirementsPage: React.FunctionComponent<{ elasticsearch.yml }} /> diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/fleet_server_requirement_page.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/fleet_server_requirement_page.tsx index c79263093abeb..28332961f37a2 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/fleet_server_requirement_page.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/fleet_server_requirement_page.tsx @@ -21,7 +21,6 @@ const FlexItemWithMinWidth = styled(EuiFlexItem)` const ContentWrapper = styled(EuiFlexGroup)` height: 100%; margin: 0 auto; - max-width: 800px; `; export const FleetServerRequirementPage = () => { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_unenroll_modal/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_unenroll_modal/index.tsx index 0b13fcc9c72be..fae88fcda4bc8 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_unenroll_modal/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_unenroll_modal/index.tsx @@ -137,7 +137,7 @@ export const AgentUnenrollAgentModal: React.FunctionComponent = ({

diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/markdown_renderers.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/markdown_renderers.tsx index cbc2f7b5f7888..2cbdfe3671c4e 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/markdown_renderers.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/markdown_renderers.tsx @@ -24,6 +24,12 @@ const REL_NOFOLLOW = 'nofollow'; /** prevents the browser from sending the current address as referrer via the Referer HTTP header */ const REL_NOREFERRER = 'noreferrer'; +// Maps deprecated code block languages to supported ones in prism.js +const CODE_LANGUAGE_OVERRIDES: Record = { + $json: 'json', + $yml: 'yml', +}; + export const markdownRenderers = { root: ({ children }: { children: React.ReactNode[] }) => ( {children} @@ -60,8 +66,17 @@ export const markdownRenderers = { ), code: ({ language, value }: { language: string; value: string }) => { - // Old packages are using `$json`, which is not valid any more with the move to prism.js - const parsedLang = language === '$json' ? 'json' : language; + let parsedLang = language; + + // Some integrations export code block content that includes language tags that have since + // been removed or deprecated in `prism.js`, the upstream depedency that handles syntax highlighting + // in EuiCodeBlock components + const languageOverride = CODE_LANGUAGE_OVERRIDES[language]; + + if (languageOverride) { + parsedLang = languageOverride; + } + return ( {value} diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/js/index.ts b/x-pack/plugins/infra/common/alerting/logs/log_threshold/index.ts similarity index 82% rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/js/index.ts rename to x-pack/plugins/infra/common/alerting/logs/log_threshold/index.ts index ef5a474df32d5..3f4cbc82c405c 100644 --- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/js/index.ts +++ b/x-pack/plugins/infra/common/alerting/logs/log_threshold/index.ts @@ -5,4 +5,5 @@ * 2.0. */ -export { processors } from './ingest'; +export * from './rule_data'; +export * from './types'; diff --git a/x-pack/plugins/infra/common/alerting/logs/log_threshold/rule_data.ts b/x-pack/plugins/infra/common/alerting/logs/log_threshold/rule_data.ts new file mode 100644 index 0000000000000..dd60739289756 --- /dev/null +++ b/x-pack/plugins/infra/common/alerting/logs/log_threshold/rule_data.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { jsonRt } from '@kbn/io-ts-utils/target/json_rt'; +import * as rt from 'io-ts'; +import { alertParamsRT as logThresholdAlertParamsRT } from './types'; + +export const serializedParamsKey = 'serialized_params'; + +export const logThresholdRuleDataNamespace = 'log_threshold_rule'; +export const logThresholdRuleDataSerializedParamsKey = `${logThresholdRuleDataNamespace}.${serializedParamsKey}` as const; + +export const logThresholdRuleDataRT = rt.type({ + [logThresholdRuleDataSerializedParamsKey]: rt.array(jsonRt.pipe(logThresholdAlertParamsRT)), +}); diff --git a/x-pack/plugins/infra/common/constants.ts b/x-pack/plugins/infra/common/constants.ts index 9362293fce82f..1c3aa550f2f62 100644 --- a/x-pack/plugins/infra/common/constants.ts +++ b/x-pack/plugins/infra/common/constants.ts @@ -11,3 +11,8 @@ export const LOGS_INDEX_PATTERN = 'logs-*,filebeat-*,kibana_sample_data_logs*'; export const TIMESTAMP_FIELD = '@timestamp'; export const METRICS_APP = 'metrics'; export const LOGS_APP = 'logs'; + +export const METRICS_FEATURE_ID = 'infrastructure'; +export const LOGS_FEATURE_ID = 'logs'; + +export type InfraFeatureId = typeof METRICS_FEATURE_ID | typeof LOGS_FEATURE_ID; diff --git a/x-pack/plugins/infra/kibana.json b/x-pack/plugins/infra/kibana.json index ec1b11c90f7a3..1e7c747887cb1 100644 --- a/x-pack/plugins/infra/kibana.json +++ b/x-pack/plugins/infra/kibana.json @@ -12,7 +12,8 @@ "visTypeTimeseries", "alerting", "triggersActionsUi", - "observability" + "observability", + "ruleRegistry" ], "optionalPlugins": ["ml", "home", "embeddable"], "server": true, @@ -26,5 +27,10 @@ "home", "ml", "embeddable" - ] + ], + "owner": { + "name": "Logs and Metrics UI", + "githubTeam": "logs-metrics-ui" + }, + "description": "This plugin visualizes data from Filebeat and Metricbeat, and integrates with other Observability solutions" } diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/index.ts b/x-pack/plugins/infra/public/alerting/log_threshold/index.ts index 5bd64de2f3ac2..0f2746b446927 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/index.ts +++ b/x-pack/plugins/infra/public/alerting/log_threshold/index.ts @@ -5,5 +5,5 @@ * 2.0. */ -export { getAlertType } from './log_threshold_alert_type'; +export * from './log_threshold_alert_type'; export { AlertDropdown } from './components/alert_dropdown'; 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 2c8a6a7ea286a..44097fd005cc7 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 @@ -7,14 +7,15 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; -import { AlertTypeModel } from '../../../../triggers_actions_ui/public'; +import { ObservabilityRuleTypeModel } from '../../../../observability/public'; import { LOG_DOCUMENT_COUNT_ALERT_TYPE_ID, PartialAlertParams, } from '../../../common/alerting/logs/log_threshold/types'; +import { formatReason } from './rule_data_formatters'; import { validateExpression } from './validation'; -export function getAlertType(): AlertTypeModel { +export function createLogThresholdAlertType(): ObservabilityRuleTypeModel { return { id: LOG_DOCUMENT_COUNT_ALERT_TYPE_ID, description: i18n.translate('xpack.infra.logs.alertFlyout.alertDescription', { @@ -33,5 +34,6 @@ export function getAlertType(): AlertTypeModel { } ), requiresAppContext: false, + format: formatReason, }; } diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/rule_data_formatters.ts b/x-pack/plugins/infra/public/alerting/log_threshold/rule_data_formatters.ts new file mode 100644 index 0000000000000..6ca081ffbc5ef --- /dev/null +++ b/x-pack/plugins/infra/public/alerting/log_threshold/rule_data_formatters.ts @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { + ALERT_EVALUATION_THRESHOLD, + ALERT_EVALUATION_VALUE, + ALERT_ID, + ALERT_START, +} from '@kbn/rule-data-utils'; +import { modifyUrl } from '@kbn/std'; +import { fold } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/function'; +import { ObservabilityRuleTypeFormatter } from '../../../../observability/public'; +import { + ComparatorToi18nMap, + logThresholdRuleDataRT, + logThresholdRuleDataSerializedParamsKey, + ratioAlertParamsRT, +} from '../../../common/alerting/logs/log_threshold'; + +export const formatReason: ObservabilityRuleTypeFormatter = ({ fields }) => { + const reason = pipe( + logThresholdRuleDataRT.decode(fields), + fold( + () => + i18n.translate('xpack.infra.logs.alerting.threshold.unknownReasonDescription', { + defaultMessage: 'unknown reason', + }), + (logThresholdRuleData) => { + const params = logThresholdRuleData[logThresholdRuleDataSerializedParamsKey][0]; + + const actualCount = fields[ALERT_EVALUATION_VALUE]; + const groupName = fields[ALERT_ID]; + const isGrouped = (params.groupBy?.length ?? 0) > 0; + const isRatio = ratioAlertParamsRT.is(params); + const thresholdCount = fields[ALERT_EVALUATION_THRESHOLD]; + const translatedComparator = ComparatorToi18nMap[params.count.comparator]; + + if (isRatio) { + return i18n.translate('xpack.infra.logs.alerting.threshold.ratioAlertReasonDescription', { + defaultMessage: + '{isGrouped, select, true{{groupName}: } false{}}The log entries ratio is {actualCount} ({translatedComparator} {thresholdCount}).', + values: { + actualCount, + translatedComparator, + groupName, + isGrouped, + thresholdCount, + }, + }); + } else { + return i18n.translate('xpack.infra.logs.alerting.threshold.countAlertReasonDescription', { + defaultMessage: + '{isGrouped, select, true{{groupName}: } false{}}{actualCount, plural, one {{actualCount} log entry} other {{actualCount} log entries} } ({translatedComparator} {thresholdCount}) match the configured conditions.', + values: { + actualCount, + translatedComparator, + groupName, + isGrouped, + thresholdCount, + }, + }); + } + } + ) + ); + + const alertStartDate = fields[ALERT_START]; + const timestamp = alertStartDate != null ? new Date(alertStartDate).valueOf() : null; + const link = modifyUrl('/app/logs/link-to/default/logs', ({ query, ...otherUrlParts }) => ({ + ...otherUrlParts, + query: { + ...query, + ...(timestamp != null ? { time: `${timestamp}` } : {}), + }, + })); + + return { + reason, + link, // TODO: refactor to URL generators + }; +}; diff --git a/x-pack/plugins/infra/public/plugin.ts b/x-pack/plugins/infra/public/plugin.ts index fd599aed5f890..d5951d9ec9915 100644 --- a/x-pack/plugins/infra/public/plugin.ts +++ b/x-pack/plugins/infra/public/plugin.ts @@ -10,21 +10,21 @@ import { AppMountParameters, PluginInitializerContext } from 'kibana/public'; import { from } from 'rxjs'; import { map } from 'rxjs/operators'; import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/public'; -import { createMetricThresholdAlertType } from './alerting/metric_threshold'; import { createInventoryMetricAlertType } from './alerting/inventory'; -import { getAlertType as getLogsAlertType } from './alerting/log_threshold'; +import { createLogThresholdAlertType } from './alerting/log_threshold'; +import { createMetricThresholdAlertType } from './alerting/metric_threshold'; +import { LOG_STREAM_EMBEDDABLE } from './components/log_stream/log_stream_embeddable'; +import { LogStreamEmbeddableFactoryDefinition } from './components/log_stream/log_stream_embeddable_factory'; +import { createMetricsFetchData, createMetricsHasData } from './metrics_overview_fetchers'; import { registerFeatures } from './register_feature'; import { - InfraClientSetupDeps, - InfraClientStartDeps, InfraClientCoreSetup, InfraClientCoreStart, InfraClientPluginClass, + InfraClientSetupDeps, + InfraClientStartDeps, } from './types'; import { getLogsHasDataFetcher, getLogsOverviewDataFetcher } from './utils/logs_overview_fetchers'; -import { createMetricsHasData, createMetricsFetchData } from './metrics_overview_fetchers'; -import { LOG_STREAM_EMBEDDABLE } from './components/log_stream/log_stream_embeddable'; -import { LogStreamEmbeddableFactoryDefinition } from './components/log_stream/log_stream_embeddable_factory'; export class Plugin implements InfraClientPluginClass { constructor(_context: PluginInitializerContext) {} @@ -35,7 +35,9 @@ export class Plugin implements InfraClientPluginClass { } pluginsSetup.triggersActionsUi.alertTypeRegistry.register(createInventoryMetricAlertType()); - pluginsSetup.triggersActionsUi.alertTypeRegistry.register(getLogsAlertType()); + pluginsSetup.observability.observabilityRuleTypeRegistry.register( + createLogThresholdAlertType() + ); pluginsSetup.triggersActionsUi.alertTypeRegistry.register(createMetricThresholdAlertType()); pluginsSetup.observability.dashboard.register({ diff --git a/x-pack/plugins/infra/server/features.ts b/x-pack/plugins/infra/server/features.ts index 91f82e82b33cd..fe0570c4950f8 100644 --- a/x-pack/plugins/infra/server/features.ts +++ b/x-pack/plugins/infra/server/features.ts @@ -10,9 +10,10 @@ import { LOG_DOCUMENT_COUNT_ALERT_TYPE_ID } from '../common/alerting/logs/log_th import { METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID } from './lib/alerting/inventory_metric_threshold/types'; import { METRIC_THRESHOLD_ALERT_TYPE_ID } from './lib/alerting/metric_threshold/types'; import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/server'; +import { LOGS_FEATURE_ID, METRICS_FEATURE_ID } from '../common/constants'; export const METRICS_FEATURE = { - id: 'infrastructure', + id: METRICS_FEATURE_ID, name: i18n.translate('xpack.infra.featureRegistry.linkInfrastructureTitle', { defaultMessage: 'Metrics', }), @@ -71,7 +72,7 @@ export const METRICS_FEATURE = { }; export const LOGS_FEATURE = { - id: 'logs', + id: LOGS_FEATURE_ID, name: i18n.translate('xpack.infra.featureRegistry.linkLogsTitle', { defaultMessage: 'Logs', }), diff --git a/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts b/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts index 1657d41d0b793..0ec071b97d7cf 100644 --- a/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts +++ b/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts @@ -20,6 +20,7 @@ import { PluginSetupContract as FeaturesPluginSetup } from '../../../../../../pl import { SpacesPluginSetup } from '../../../../../../plugins/spaces/server'; import { PluginSetupContract as AlertingPluginContract } from '../../../../../alerting/server'; import { MlPluginSetup } from '../../../../../ml/server'; +import { RuleRegistryPluginSetupContract } from '../../../../../rule_registry/server'; export interface InfraServerPluginSetupDeps { data: DataPluginSetup; @@ -29,6 +30,7 @@ export interface InfraServerPluginSetupDeps { visTypeTimeseries: VisTypeTimeseriesSetup; features: FeaturesPluginSetup; alerting: AlertingPluginContract; + ruleRegistry: RuleRegistryPluginSetupContract; ml?: MlPluginSetup; } 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 f9d0b5575abfc..241931e610af0 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 @@ -5,59 +5,64 @@ * 2.0. */ +import { estypes } from '@elastic/elasticsearch'; import { i18n } from '@kbn/i18n'; +import { ALERT_EVALUATION_THRESHOLD, ALERT_EVALUATION_VALUE } from '@kbn/rule-data-utils'; import { ElasticsearchClient } from 'kibana/server'; -import { estypes } from '@elastic/elasticsearch'; import { - AlertExecutorOptions, - AlertServices, + ActionGroup, + ActionGroupIdsOf, AlertInstance, - AlertTypeParams, - AlertTypeState, AlertInstanceContext, AlertInstanceState, - ActionGroup, - ActionGroupIdsOf, + AlertTypeState, } from '../../../../../alerting/server'; import { + logThresholdRuleDataRT, + logThresholdRuleDataSerializedParamsKey, +} from '../../../../common/alerting/logs/log_threshold'; +import { + AlertParams, + alertParamsRT, AlertStates, Comparator, - AlertParams, + CountAlertParams, + CountCriteria, Criterion, - GroupedSearchQueryResponseRT, - UngroupedSearchQueryResponseRT, - UngroupedSearchQueryResponse, + getDenominator, + getNumerator, GroupedSearchQueryResponse, - alertParamsRT, - isRatioAlertParams, + GroupedSearchQueryResponseRT, hasGroupBy, - getNumerator, - getDenominator, - CountCriteria, - CountAlertParams, - RatioAlertParams, - isOptimizedGroupedSearchQueryResponse, isOptimizableGroupedThreshold, + isOptimizedGroupedSearchQueryResponse, + isRatioAlertParams, + RatioAlertParams, + UngroupedSearchQueryResponse, + UngroupedSearchQueryResponseRT, } from '../../../../common/alerting/logs/log_threshold/types'; -import { InfraBackendLibs } from '../../infra_types'; -import { getIntervalInSeconds } from '../../../utils/get_interval_in_seconds'; +import { resolveLogSourceConfiguration } from '../../../../common/log_sources'; import { decodeOrThrow } from '../../../../common/runtime_types'; +import { getIntervalInSeconds } from '../../../utils/get_interval_in_seconds'; +import { InfraBackendLibs } from '../../infra_types'; import { UNGROUPED_FACTORY_KEY } from '../common/utils'; -import { resolveLogSourceConfiguration } from '../../../../common/log_sources'; -type LogThresholdActionGroups = ActionGroupIdsOf; -type LogThresholdAlertServices = AlertServices< - AlertInstanceState, - AlertInstanceContext, - LogThresholdActionGroups ->; -type LogThresholdAlertExecutorOptions = AlertExecutorOptions< - AlertTypeParams, - AlertTypeState, - AlertInstanceState, - AlertInstanceContext, +export type LogThresholdActionGroups = ActionGroupIdsOf; +export type LogThresholdAlertTypeParams = AlertParams; +export type LogThresholdAlertTypeState = AlertTypeState; // no specific state used +export type LogThresholdAlertInstanceState = AlertInstanceState; // no specific state used +export type LogThresholdAlertInstanceContext = AlertInstanceContext; // no specific instance context used + +type LogThresholdAlertInstance = AlertInstance< + LogThresholdAlertInstanceState, + LogThresholdAlertInstanceContext, LogThresholdActionGroups >; +type LogThresholdAlertInstanceFactory = ( + id: string, + threshold: number, + value: number +) => LogThresholdAlertInstance; const COMPOSITE_GROUP_SIZE = 2000; @@ -75,9 +80,26 @@ const checkValueAgainstComparatorMap: { // With forks for group_by vs ungrouped, and ratio vs non-ratio. export const createLogThresholdExecutor = (libs: InfraBackendLibs) => - async function ({ services, params }: LogThresholdAlertExecutorOptions) { - const { alertInstanceFactory, savedObjectsClient, scopedClusterClient } = services; + libs.logsRules.createLifecycleRuleExecutor< + LogThresholdAlertTypeParams, + LogThresholdAlertTypeState, + LogThresholdAlertInstanceState, + LogThresholdAlertInstanceContext, + LogThresholdActionGroups + >(async ({ services, params }) => { + const { alertWithLifecycle, savedObjectsClient, scopedClusterClient } = services; const { sources } = libs; + const alertInstanceFactory: LogThresholdAlertInstanceFactory = (id, threshold, value) => + alertWithLifecycle({ + id, + fields: { + [ALERT_EVALUATION_THRESHOLD]: threshold, + [ALERT_EVALUATION_VALUE]: value, + ...logThresholdRuleDataRT.encode({ + [logThresholdRuleDataSerializedParamsKey]: [params], + }), + }, + }); const sourceConfiguration = await sources.getSourceConfiguration(savedObjectsClient, 'default'); const { indices, timestampField, runtimeMappings } = await resolveLogSourceConfiguration( @@ -113,7 +135,7 @@ export const createLogThresholdExecutor = (libs: InfraBackendLibs) => } catch (e) { throw new Error(e); } - }; + }); async function executeAlert( alertParams: CountAlertParams, @@ -121,7 +143,7 @@ async function executeAlert( indexPattern: string, runtimeMappings: estypes.MappingRuntimeFields, esClient: ElasticsearchClient, - alertInstanceFactory: LogThresholdAlertServices['alertInstanceFactory'] + alertInstanceFactory: LogThresholdAlertInstanceFactory ) { const query = getESQuery(alertParams, timestampField, indexPattern, runtimeMappings); @@ -152,7 +174,7 @@ async function executeRatioAlert( indexPattern: string, runtimeMappings: estypes.MappingRuntimeFields, esClient: ElasticsearchClient, - alertInstanceFactory: LogThresholdAlertServices['alertInstanceFactory'] + alertInstanceFactory: LogThresholdAlertInstanceFactory ) { // Ratio alert params are separated out into two standard sets of alert params const numeratorParams: AlertParams = { @@ -214,14 +236,14 @@ const getESQuery = ( export const processUngroupedResults = ( results: UngroupedSearchQueryResponse, params: CountAlertParams, - alertInstanceFactory: LogThresholdAlertExecutorOptions['services']['alertInstanceFactory'], + alertInstanceFactory: LogThresholdAlertInstanceFactory, alertInstaceUpdater: AlertInstanceUpdater ) => { const { count, criteria } = params; const documentCount = results.hits.total.value; if (checkValueAgainstComparatorMap[count.comparator](documentCount, count.value)) { - const alertInstance = alertInstanceFactory(UNGROUPED_FACTORY_KEY); + const alertInstance = alertInstanceFactory(UNGROUPED_FACTORY_KEY, count.value, documentCount); alertInstaceUpdater(alertInstance, AlertStates.ALERT, [ { actionGroup: FIRED_ACTIONS.id, @@ -240,7 +262,7 @@ export const processUngroupedRatioResults = ( numeratorResults: UngroupedSearchQueryResponse, denominatorResults: UngroupedSearchQueryResponse, params: RatioAlertParams, - alertInstanceFactory: LogThresholdAlertExecutorOptions['services']['alertInstanceFactory'], + alertInstanceFactory: LogThresholdAlertInstanceFactory, alertInstaceUpdater: AlertInstanceUpdater ) => { const { count, criteria } = params; @@ -250,7 +272,7 @@ export const processUngroupedRatioResults = ( const ratio = getRatio(numeratorCount, denominatorCount); if (ratio !== undefined && checkValueAgainstComparatorMap[count.comparator](ratio, count.value)) { - const alertInstance = alertInstanceFactory(UNGROUPED_FACTORY_KEY); + const alertInstance = alertInstanceFactory(UNGROUPED_FACTORY_KEY, count.value, ratio); alertInstaceUpdater(alertInstance, AlertStates.ALERT, [ { actionGroup: FIRED_ACTIONS.id, @@ -308,7 +330,7 @@ const getReducedGroupByResults = ( export const processGroupByResults = ( results: GroupedSearchQueryResponse['aggregations']['groups']['buckets'], params: CountAlertParams, - alertInstanceFactory: LogThresholdAlertExecutorOptions['services']['alertInstanceFactory'], + alertInstanceFactory: LogThresholdAlertInstanceFactory, alertInstaceUpdater: AlertInstanceUpdater ) => { const { count, criteria } = params; @@ -319,7 +341,7 @@ export const processGroupByResults = ( const documentCount = group.documentCount; if (checkValueAgainstComparatorMap[count.comparator](documentCount, count.value)) { - const alertInstance = alertInstanceFactory(group.name); + const alertInstance = alertInstanceFactory(group.name, count.value, documentCount); alertInstaceUpdater(alertInstance, AlertStates.ALERT, [ { actionGroup: FIRED_ACTIONS.id, @@ -339,7 +361,7 @@ export const processGroupByRatioResults = ( numeratorResults: GroupedSearchQueryResponse['aggregations']['groups']['buckets'], denominatorResults: GroupedSearchQueryResponse['aggregations']['groups']['buckets'], params: RatioAlertParams, - alertInstanceFactory: LogThresholdAlertExecutorOptions['services']['alertInstanceFactory'], + alertInstanceFactory: LogThresholdAlertInstanceFactory, alertInstaceUpdater: AlertInstanceUpdater ) => { const { count, criteria } = params; @@ -360,7 +382,7 @@ export const processGroupByRatioResults = ( ratio !== undefined && checkValueAgainstComparatorMap[count.comparator](ratio, count.value) ) { - const alertInstance = alertInstanceFactory(numeratorGroup.name); + const alertInstance = alertInstanceFactory(numeratorGroup.name, count.value, ratio); alertInstaceUpdater(alertInstance, AlertStates.ALERT, [ { actionGroup: FIRED_ACTIONS.id, 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 62d92d0487ff7..3d0bac3dd2bf5 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 @@ -6,14 +6,7 @@ */ import { i18n } from '@kbn/i18n'; -import { - PluginSetupContract, - AlertTypeParams, - AlertTypeState, - AlertInstanceContext, - AlertInstanceState, - ActionGroupIdsOf, -} from '../../../../../alerting/server'; +import { PluginSetupContract } from '../../../../../alerting/server'; import { createLogThresholdExecutor, FIRED_ACTIONS } from './log_threshold_executor'; import { LOG_DOCUMENT_COUNT_ALERT_TYPE_ID, @@ -88,13 +81,7 @@ export async function registerLogThresholdAlertType( ); } - alertingPlugin.registerType< - AlertTypeParams, - AlertTypeState, - AlertInstanceState, - AlertInstanceContext, - ActionGroupIdsOf - >({ + alertingPlugin.registerType({ id: LOG_DOCUMENT_COUNT_ALERT_TYPE_ID, name: i18n.translate('xpack.infra.logs.alertName', { defaultMessage: 'Log threshold', diff --git a/x-pack/plugins/infra/server/lib/infra_types.ts b/x-pack/plugins/infra/server/lib/infra_types.ts index 0c57ff2e05847..332a2e499977b 100644 --- a/x-pack/plugins/infra/server/lib/infra_types.ts +++ b/x-pack/plugins/infra/server/lib/infra_types.ts @@ -5,15 +5,16 @@ * 2.0. */ +import { handleEsError } from '../../../../../src/plugins/es_ui_shared/server'; +import { InfraConfig } from '../plugin'; +import { GetLogQueryFields } from '../services/log_queries/get_log_query_fields'; +import { RulesServiceSetup } from '../services/rules'; +import { KibanaFramework } from './adapters/framework/kibana_framework_adapter'; import { InfraFieldsDomain } from './domains/fields_domain'; import { InfraLogEntriesDomain } from './domains/log_entries_domain'; import { InfraMetricsDomain } from './domains/metrics_domain'; import { InfraSources } from './sources'; import { InfraSourceStatus } from './source_status'; -import { InfraConfig } from '../plugin'; -import { KibanaFramework } from './adapters/framework/kibana_framework_adapter'; -import { GetLogQueryFields } from '../services/log_queries/get_log_query_fields'; -import { handleEsError } from '../../../../../src/plugins/es_ui_shared/server'; export interface InfraDomainLibs { fields: InfraFieldsDomain; @@ -28,4 +29,6 @@ export interface InfraBackendLibs extends InfraDomainLibs { sourceStatus: InfraSourceStatus; getLogQueryFields: GetLogQueryFields; handleEsError: typeof handleEsError; + logsRules: RulesServiceSetup; + metricsRules: RulesServiceSetup; } diff --git a/x-pack/plugins/infra/server/plugin.ts b/x-pack/plugins/infra/server/plugin.ts index 7c5666049bd60..de445affc178e 100644 --- a/x-pack/plugins/infra/server/plugin.ts +++ b/x-pack/plugins/infra/server/plugin.ts @@ -8,7 +8,9 @@ import { Server } from '@hapi/hapi'; import { schema, TypeOf } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; +import { Logger } from '@kbn/logging'; import { CoreSetup, PluginInitializerContext, Plugin } from 'src/core/server'; +import { LOGS_FEATURE_ID, METRICS_FEATURE_ID } from '../common/constants'; import { InfraStaticSourceConfiguration } from '../common/source_configuration/source_configuration'; import { inventoryViewSavedObjectType } from '../common/saved_objects/inventory_view'; import { metricsExplorerViewSavedObjectType } from '../common/saved_objects/metrics_explorer_view'; @@ -32,6 +34,7 @@ import { InfraPluginRequestHandlerContext } from './types'; import { UsageCollector } from './usage/usage_collector'; import { createGetLogQueryFields } from './services/log_queries/get_log_query_fields'; import { handleEsError } from '../../../../src/plugins/es_ui_shared/server'; +import { RulesService } from './services/rules'; export const config = { schema: schema.object({ @@ -82,9 +85,25 @@ export interface InfraPluginSetup { export class InfraServerPlugin implements Plugin { public config: InfraConfig; public libs: InfraBackendLibs | undefined; + public logger: Logger; + + private logsRules: RulesService; + private metricsRules: RulesService; constructor(context: PluginInitializerContext) { this.config = context.config.get(); + this.logger = context.logger.get(); + + this.logsRules = new RulesService( + LOGS_FEATURE_ID, + 'observability.logs', + this.logger.get('logsRules') + ); + this.metricsRules = new RulesService( + METRICS_FEATURE_ID, + 'observability.metrics', + this.logger.get('metricsRules') + ); } setup(core: CoreSetup, plugins: InfraServerPluginSetupDeps) { @@ -126,6 +145,8 @@ export class InfraServerPlugin implements Plugin { ...domainLibs, getLogQueryFields: createGetLogQueryFields(sources, framework), handleEsError, + logsRules: this.logsRules.setup(core, plugins), + metricsRules: this.metricsRules.setup(core, plugins), }; plugins.features.registerKibanaFeature(METRICS_FEATURE); diff --git a/x-pack/plugins/canvas/public/components/error/index.ts b/x-pack/plugins/infra/server/services/rules/index.ts similarity index 81% rename from x-pack/plugins/canvas/public/components/error/index.ts rename to x-pack/plugins/infra/server/services/rules/index.ts index 65c4af8a369ae..eaa3d0da493e5 100644 --- a/x-pack/plugins/canvas/public/components/error/index.ts +++ b/x-pack/plugins/infra/server/services/rules/index.ts @@ -5,4 +5,5 @@ * 2.0. */ -export { Error } from './error'; +export * from './rules_service'; +export * from './types'; diff --git a/x-pack/plugins/infra/server/services/rules/rule_data_client.ts b/x-pack/plugins/infra/server/services/rules/rule_data_client.ts new file mode 100644 index 0000000000000..d693be40f10d0 --- /dev/null +++ b/x-pack/plugins/infra/server/services/rules/rule_data_client.ts @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { once } from 'lodash'; +import { CoreSetup, Logger } from 'src/core/server'; +import { TECHNICAL_COMPONENT_TEMPLATE_NAME } from '../../../../rule_registry/common/assets'; +import { RuleRegistryPluginSetupContract } from '../../../../rule_registry/server'; +import { logThresholdRuleDataNamespace } from '../../../common/alerting/logs/log_threshold'; +import type { InfraFeatureId } from '../../../common/constants'; +import { RuleRegistrationContext, RulesServiceStartDeps } from './types'; + +export const createRuleDataClient = ({ + ownerFeatureId, + registrationContext, + getStartServices, + logger, + ruleDataService, +}: { + ownerFeatureId: InfraFeatureId; + registrationContext: RuleRegistrationContext; + getStartServices: CoreSetup['getStartServices']; + logger: Logger; + ruleDataService: RuleRegistryPluginSetupContract['ruleDataService']; +}) => { + const initializeRuleDataTemplates = once(async () => { + const componentTemplateName = ruleDataService.getFullAssetName( + `${registrationContext}-mappings` + ); + + const indexNamePattern = ruleDataService.getFullAssetName(`${registrationContext}*`); + + if (!ruleDataService.isWriteEnabled()) { + return; + } + + await ruleDataService.createOrUpdateComponentTemplate({ + name: componentTemplateName, + body: { + template: { + settings: { + number_of_shards: 1, + }, + mappings: { + properties: { + [logThresholdRuleDataNamespace]: { + properties: { + serialized_params: { + type: 'keyword', + index: false, + }, + }, + }, + }, + }, + }, + }, + }); + + await ruleDataService.createOrUpdateIndexTemplate({ + name: ruleDataService.getFullAssetName(registrationContext), + body: { + index_patterns: [indexNamePattern], + composed_of: [ + ruleDataService.getFullAssetName(TECHNICAL_COMPONENT_TEMPLATE_NAME), + componentTemplateName, + ], + }, + }); + + await ruleDataService.updateIndexMappingsMatchingPattern(indexNamePattern); + }); + + // initialize eagerly + const initializeRuleDataTemplatesPromise = initializeRuleDataTemplates().catch((err) => { + logger.error(err); + }); + + return ruleDataService.getRuleDataClient( + ownerFeatureId, + ruleDataService.getFullAssetName(registrationContext), + () => initializeRuleDataTemplatesPromise + ); +}; diff --git a/x-pack/plugins/infra/server/services/rules/rules_service.ts b/x-pack/plugins/infra/server/services/rules/rules_service.ts new file mode 100644 index 0000000000000..9341fc59d75b8 --- /dev/null +++ b/x-pack/plugins/infra/server/services/rules/rules_service.ts @@ -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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CoreSetup, Logger } from 'src/core/server'; +import { createLifecycleExecutor } from '../../../../rule_registry/server'; +import { InfraFeatureId } from '../../../common/constants'; +import { createRuleDataClient } from './rule_data_client'; +import { + RuleRegistrationContext, + RulesServiceSetup, + RulesServiceSetupDeps, + RulesServiceStart, + RulesServiceStartDeps, +} from './types'; + +export class RulesService { + constructor( + public readonly ownerFeatureId: InfraFeatureId, + public readonly registrationContext: RuleRegistrationContext, + private readonly logger: Logger + ) {} + + public setup( + core: CoreSetup, + setupDeps: RulesServiceSetupDeps + ): RulesServiceSetup { + const ruleDataClient = createRuleDataClient({ + getStartServices: core.getStartServices, + logger: this.logger, + ownerFeatureId: this.ownerFeatureId, + registrationContext: this.registrationContext, + ruleDataService: setupDeps.ruleRegistry.ruleDataService, + }); + + const createLifecycleRuleExecutor = createLifecycleExecutor(this.logger, ruleDataClient); + + return { + createLifecycleRuleExecutor, + ruleDataClient, + }; + } + + public start(_startDeps: RulesServiceStartDeps): RulesServiceStart { + return {}; + } +} diff --git a/x-pack/plugins/infra/server/services/rules/types.ts b/x-pack/plugins/infra/server/services/rules/types.ts new file mode 100644 index 0000000000000..b67b79ee5d3c2 --- /dev/null +++ b/x-pack/plugins/infra/server/services/rules/types.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { PluginSetupContract as AlertingPluginSetup } from '../../../../alerting/server'; +import { + createLifecycleExecutor, + RuleDataClient, + RuleRegistryPluginSetupContract, +} from '../../../../rule_registry/server'; + +type LifecycleRuleExecutorCreator = ReturnType; + +export interface RulesServiceSetupDeps { + alerting: AlertingPluginSetup; + ruleRegistry: RuleRegistryPluginSetupContract; +} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface RulesServiceStartDeps {} + +export interface RulesServiceSetup { + createLifecycleRuleExecutor: LifecycleRuleExecutorCreator; + ruleDataClient: RuleDataClient; +} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface RulesServiceStart {} + +export type RuleRegistrationContext = 'observability.logs' | 'observability.metrics'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/network_direction.test.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/network_direction.test.tsx new file mode 100644 index 0000000000000..7a4c55d6f5e02 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/network_direction.test.tsx @@ -0,0 +1,180 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { act } from 'react-dom/test-utils'; +import { setup, SetupResult, getProcessorValue } from './processor.helpers'; + +// Default parameter values automatically added to the network direction processor when saved +const defaultNetworkDirectionParameters = { + if: undefined, + tag: undefined, + source_ip: undefined, + description: undefined, + target_field: undefined, + ignore_missing: undefined, + ignore_failure: undefined, + destination_ip: undefined, + internal_networks: undefined, + internal_networks_field: undefined, +}; + +const NETWORK_DIRECTION_TYPE = 'network_direction'; + +describe('Processor: Network Direction', () => { + let onUpdate: jest.Mock; + let testBed: SetupResult; + + beforeAll(() => { + jest.useFakeTimers(); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + beforeEach(async () => { + onUpdate = jest.fn(); + + await act(async () => { + testBed = await setup({ + value: { + processors: [], + }, + onFlyoutOpen: jest.fn(), + onUpdate, + }); + }); + + testBed.component.update(); + + // Open flyout to add new processor + testBed.actions.addProcessor(); + // Add type (the other fields are not visible until a type is selected) + await testBed.actions.addProcessorType(NETWORK_DIRECTION_TYPE); + }); + + test('prevents form submission if internal_network field is not provided', async () => { + const { + actions: { saveNewProcessor }, + form, + } = testBed; + + // Click submit button with only the type defined + await saveNewProcessor(); + + // Expect form error as "field" is required parameter + expect(form.getErrorsMessages()).toEqual(['A field value is required.']); + }); + + test('saves with default parameter values', async () => { + const { + actions: { saveNewProcessor }, + find, + component, + } = testBed; + + // Add "networkDirectionField" value (required) + await act(async () => { + find('networkDirectionField.input').simulate('change', [{ label: 'loopback' }]); + }); + component.update(); + + // Save the field + await saveNewProcessor(); + + const processors = getProcessorValue(onUpdate, NETWORK_DIRECTION_TYPE); + expect(processors[0][NETWORK_DIRECTION_TYPE]).toEqual({ + ...defaultNetworkDirectionParameters, + internal_networks: ['loopback'], + }); + }); + + test('allows to set internal_networks_field', async () => { + const { + actions: { saveNewProcessor }, + form, + find, + } = testBed; + + find('toggleCustomField').simulate('click'); + + form.setInputValue('networkDirectionField.input', 'internal_networks_field'); + + // Save the field with new changes + await saveNewProcessor(); + + const processors = getProcessorValue(onUpdate, NETWORK_DIRECTION_TYPE); + expect(processors[0][NETWORK_DIRECTION_TYPE]).toEqual({ + ...defaultNetworkDirectionParameters, + internal_networks_field: 'internal_networks_field', + }); + }); + + test('allows to set just internal_networks_field or internal_networks', async () => { + const { + actions: { saveNewProcessor }, + form, + find, + component, + } = testBed; + + // Set internal_networks field + await act(async () => { + find('networkDirectionField.input').simulate('change', [{ label: 'loopback' }]); + }); + component.update(); + + // Toggle to internal_networks_field and set a random value + find('toggleCustomField').simulate('click'); + form.setInputValue('networkDirectionField.input', 'internal_networks_field'); + + // Save the field with new changes + await saveNewProcessor(); + + const processors = getProcessorValue(onUpdate, NETWORK_DIRECTION_TYPE); + expect(processors[0][NETWORK_DIRECTION_TYPE]).toEqual({ + ...defaultNetworkDirectionParameters, + internal_networks_field: 'internal_networks_field', + }); + }); + + test('allows optional parameters to be set', async () => { + const { + actions: { saveNewProcessor }, + form, + find, + component, + } = testBed; + + // Add "networkDirectionField" value (required) + await act(async () => { + find('networkDirectionField.input').simulate('change', [{ label: 'loopback' }]); + }); + component.update(); + + // Set optional parameteres + form.toggleEuiSwitch('ignoreMissingSwitch.input'); + form.toggleEuiSwitch('ignoreFailureSwitch.input'); + form.setInputValue('sourceIpField.input', 'source.ip'); + form.setInputValue('targetField.input', 'target_field'); + form.setInputValue('destinationIpField.input', 'destination.ip'); + + // Save the field with new changes + await saveNewProcessor(); + + const processors = getProcessorValue(onUpdate, NETWORK_DIRECTION_TYPE); + expect(processors[0][NETWORK_DIRECTION_TYPE]).toEqual({ + ...defaultNetworkDirectionParameters, + ignore_failure: true, + ignore_missing: false, + source_ip: 'source.ip', + target_field: 'target_field', + destination_ip: 'destination.ip', + internal_networks: ['loopback'], + }); + }); +}); diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx index 24e1ddce008ea..e4024e4ec67f4 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx @@ -171,6 +171,10 @@ type TestSubject = | 'regexFileField.input' | 'valueFieldInput' | 'mediaTypeSelectorField' + | 'networkDirectionField.input' + | 'sourceIpField.input' + | 'destinationIpField.input' + | 'toggleCustomField' | 'ignoreEmptyField.input' | 'overrideField.input' | 'fieldsValueField.input' diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/index.ts index 5e3e5f82478bd..f5eb1ab3ec59b 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/index.ts +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/index.ts @@ -28,6 +28,7 @@ export { Join } from './join'; export { Json } from './json'; export { Kv } from './kv'; export { Lowercase } from './lowercase'; +export { NetworkDirection } from './network_direction'; export { Pipeline } from './pipeline'; export { RegisteredDomain } from './registered_domain'; export { Remove } from './remove'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/network_direction.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/network_direction.tsx new file mode 100644 index 0000000000000..2026a77bc6566 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/network_direction.tsx @@ -0,0 +1,242 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FunctionComponent, useState, useCallback, useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; +import { isEmpty } from 'lodash'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiButtonEmpty, EuiCode } from '@elastic/eui'; + +import { + FIELD_TYPES, + UseField, + useFormContext, + Field, + FieldHook, + FieldConfig, + SerializerFunc, +} from '../../../../../../shared_imports'; +import { FieldsConfig, from, to } from './shared'; +import { TargetField } from './common_fields/target_field'; +import { IgnoreMissingField } from './common_fields/ignore_missing_field'; + +interface InternalNetworkTypes { + internal_networks: string[]; + internal_networks_field: string; +} + +type InternalNetworkFields = { + [K in keyof InternalNetworkTypes]: FieldHook; +}; + +const internalNetworkValues: string[] = [ + 'loopback', + 'unicast', + 'global_unicast', + 'multicast', + 'interface_local_multicast', + 'link_local_unicast', + 'link_local_multicast', + 'link_local_multicast', + 'private', + 'public', + 'unspecified', +]; + +const fieldsConfig: FieldsConfig = { + /* Optional fields config */ + source_ip: { + type: FIELD_TYPES.TEXT, + serializer: from.emptyStringToUndefined, + label: i18n.translate('xpack.ingestPipelines.pipelineEditor.networkDirection.sourceIpLabel', { + defaultMessage: 'Source IP (optional)', + }), + helpText: ( + {'source.ip'}, + }} + /> + ), + }, + destination_ip: { + type: FIELD_TYPES.TEXT, + serializer: from.emptyStringToUndefined, + label: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.networkDirection.destinationIpLabel', + { + defaultMessage: 'Destination IP (optional)', + } + ), + helpText: ( + {'destination.ip'}, + }} + /> + ), + }, +}; + +const getInternalNetworkConfig: ( + toggleCustom: () => void +) => Record< + keyof InternalNetworkFields, + { + path: string; + config?: FieldConfig; + euiFieldProps?: Record; + labelAppend: JSX.Element; + } +> = (toggleCustom: () => void) => ({ + internal_networks: { + path: 'fields.internal_networks', + euiFieldProps: { + noSuggestions: false, + options: internalNetworkValues.map((label) => ({ label })), + }, + config: { + type: FIELD_TYPES.COMBO_BOX, + deserializer: to.arrayOfStrings, + serializer: from.optionalArrayOfStrings, + fieldsToValidateOnChange: ['fields.internal_networks', 'fields.internal_networks_field'], + validations: [ + { + validator: ({ value, path, formData }) => { + if (isEmpty(value) && isEmpty(formData['fields.internal_networks_field'])) { + return { path, message: 'A field value is required.' }; + } + }, + }, + ], + label: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.networkDirection.internalNetworksLabel', + { + defaultMessage: 'Internal networks', + } + ), + helpText: ( + + ), + }, + labelAppend: ( + + {i18n.translate('xpack.ingestPipelines.pipelineEditor.internalNetworkCustomLabel', { + defaultMessage: 'Use custom field', + })} + + ), + key: 'preset', + }, + internal_networks_field: { + path: 'fields.internal_networks_field', + config: { + type: FIELD_TYPES.TEXT, + serializer: from.emptyStringToUndefined, + fieldsToValidateOnChange: ['fields.internal_networks', 'fields.internal_networks_field'], + validations: [ + { + validator: ({ value, path, formData }) => { + if (isEmpty(value) && isEmpty(formData['fields.internal_networks'])) { + return { path, message: 'A field value is required.' }; + } + }, + }, + ], + label: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.networkDirection.internalNetworksFieldLabel', + { + defaultMessage: 'Internal networks field', + } + ), + helpText: ( + {'internal_networks'}, + }} + /> + ), + }, + labelAppend: ( + + {i18n.translate('xpack.ingestPipelines.pipelineEditor.internalNetworkPredefinedLabel', { + defaultMessage: 'Use preset field', + })} + + ), + key: 'custom', + }, +}); + +export const NetworkDirection: FunctionComponent = () => { + const { getFieldDefaultValue } = useFormContext(); + const isInternalNetowrksFieldDefined = + getFieldDefaultValue('fields.internal_networks_field') !== undefined; + const [isCustom, setIsCustom] = useState(isInternalNetowrksFieldDefined); + + const toggleCustom = useCallback(() => { + setIsCustom((prev) => !prev); + }, []); + + const internalNetworkFieldProps = useMemo( + () => + isCustom + ? getInternalNetworkConfig(toggleCustom).internal_networks_field + : getInternalNetworkConfig(toggleCustom).internal_networks, + [isCustom, toggleCustom] + ); + + return ( + <> + + + + + {'network.direction'}, + }} + /> + } + /> + + + + } + /> + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/map_processor_type_to_form.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/map_processor_type_to_form.tsx index 983fb0ea67bb0..e6ca465bf1a02 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/map_processor_type_to_form.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/map_processor_type_to_form.tsx @@ -34,6 +34,7 @@ import { Json, Kv, Lowercase, + NetworkDirection, Pipeline, RegisteredDomain, Remove, @@ -517,6 +518,23 @@ export const mapProcessorTypeToDescriptor: MapProcessorTypeToDescriptor = { }, }), }, + network_direction: { + FieldsComponent: NetworkDirection, + docLinkPath: '/network-direction-processor.html', + label: i18n.translate('xpack.ingestPipelines.processors.label.networkDirection', { + defaultMessage: 'Network Direction', + }), + typeDescription: i18n.translate( + 'xpack.ingestPipelines.processors.description.networkDirection', + { + defaultMessage: 'Calculates the network direction given a source IP address.', + } + ), + getDefaultDescription: () => + i18n.translate('xpack.ingestPipelines.processors.defaultDescription.networkDirection', { + defaultMessage: 'Calculates the network direction given a source IP address.', + }), + }, pipeline: { FieldsComponent: Pipeline, docLinkPath: '/pipeline-processor.html', diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/context/processors_context.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/context/processors_context.tsx index 0c43297e811d3..ddf996de7805c 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/context/processors_context.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/context/processors_context.tsx @@ -151,7 +151,13 @@ export const PipelineProcessorsContextProvider: FunctionComponent = ({ break; case 'managingProcessor': // These are the option names we get back from our UI - const knownOptionNames = Object.keys(processorTypeAndOptions.options); + const knownOptionNames = [ + ...Object.keys(processorTypeAndOptions.options), + // We manually add fields that we **don't** want to be treated as "unknownOptions" + 'internal_networks', + 'internal_networks_field', + ]; + // The processor that we are updating may have options configured the UI does not know about const unknownOptions = omit(mode.arg.processor.options, knownOptionNames); // In order to keep the options we don't get back from our UI, we merge the known and unknown options diff --git a/x-pack/plugins/ingest_pipelines/public/shared_imports.ts b/x-pack/plugins/ingest_pipelines/public/shared_imports.ts index 8ed57221a1395..29be11430bf64 100644 --- a/x-pack/plugins/ingest_pipelines/public/shared_imports.ts +++ b/x-pack/plugins/ingest_pipelines/public/shared_imports.ts @@ -43,6 +43,7 @@ export { ArrayItem, FormHook, useFormContext, + UseMultiFields, FormDataProvider, OnFormUpdateArg, FieldConfig, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula.test.tsx index 19d91c1006cf0..936f1e477057d 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula.test.tsx @@ -35,7 +35,7 @@ const operationDefinitionMap: Record = { }), } as unknown) as GenericOperationDefinition, terms: { input: 'field' } as GenericOperationDefinition, - sum: { input: 'field' } as GenericOperationDefinition, + sum: { input: 'field', filterable: true } as GenericOperationDefinition, last_value: { input: 'field' } as GenericOperationDefinition, max: { input: 'field' } as GenericOperationDefinition, count: ({ @@ -928,6 +928,63 @@ invalid: " ).toEqual(['The operation average does not accept any parameter']); }); + it('returns an error if first argument type is passed multiple times', () => { + const formulas = [ + 'average(bytes, bytes)', + "sum(bytes, kql='category.keyword: *', bytes)", + 'moving_average(average(bytes), average(bytes))', + "moving_average(average(bytes), kql='category.keyword: *', average(bytes))", + 'moving_average(average(bytes, bytes), count())', + 'moving_average(moving_average(average(bytes, bytes), count(), count()))', + ]; + for (const formula of formulas) { + expect( + formulaOperation.getErrorMessage!( + getNewLayerWithFormula(formula), + 'col1', + indexPattern, + operationDefinitionMap + ) + ).toEqual( + expect.arrayContaining([ + expect.stringMatching( + /The operation (moving_average|average|sum) in the Formula requires a single (field|metric), found:/ + ), + ]) + ); + } + }); + + it('returns an error if a function received an argument of the wrong argument type in any position', () => { + const formulas = [ + 'average(bytes, count())', + "sum(bytes, kql='category.keyword: *', count(), count())", + 'average(bytes, bytes + 1)', + 'average(count(), bytes)', + 'moving_average(average(bytes), bytes)', + 'moving_average(bytes, bytes)', + 'moving_average(average(bytes), window=7, bytes)', + 'moving_average(window=7, bytes)', + "moving_average(kql='category.keyword: *', bytes)", + ]; + for (const formula of formulas) { + expect( + formulaOperation.getErrorMessage!( + getNewLayerWithFormula(formula), + 'col1', + indexPattern, + operationDefinitionMap + ) + ).toEqual( + expect.arrayContaining([ + expect.stringMatching( + /The operation (moving_average|average|sum) in the Formula does not support (metric|field) parameters, found:/ + ), + ]) + ); + } + }); + it('returns an error if the parameter passed to an operation is of the wrong type', () => { expect( formulaOperation.getErrorMessage!( @@ -1087,6 +1144,14 @@ invalid: " ) ).toEqual([`The first argument for ${fn} should be a field name. Found no field`]); } + expect( + formulaOperation.getErrorMessage!( + getNewLayerWithFormula(`sum(kql='category.keyword: *')`), + 'col1', + indexPattern, + operationDefinitionMap + ) + ).toEqual([`The first argument for sum should be a field name. Found category.keyword: *`]); }); it("returns a clear error when there's a missing function for a fullReference operation", () => { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/util.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/util.ts index 445df21a6067e..6d0a585db048f 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/util.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/util.ts @@ -455,7 +455,7 @@ Example: Calculate area based on side length }, }; -export function isMathNode(node: TinymathAST) { +export function isMathNode(node: TinymathAST | string) { return isObject(node) && node.type === 'function' && tinymathFunctions[node.name]; } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/validation.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/validation.ts index 5b7a9beaa4e32..d65ef5ada8b37 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/validation.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/validation.ts @@ -7,7 +7,7 @@ import { isObject, partition } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { parse, TinymathLocation } from '@kbn/tinymath'; +import { parse, TinymathLocation, TinymathVariable } from '@kbn/tinymath'; import type { TinymathAST, TinymathFunction, TinymathNamedArgument } from '@kbn/tinymath'; import { esKuery, esQuery } from '../../../../../../../../src/plugins/data/public'; import { @@ -63,6 +63,19 @@ interface ValidationErrors { message: string; type: {}; }; + tooManyFirstArguments: { + message: string; + type: { + operation: string; + type: string; + text: string; + supported?: number; + }; + }; + wrongArgument: { + message: string; + type: { operation: string; text: string; type: string }; + }; } type ErrorTypes = keyof ValidationErrors; @@ -276,6 +289,25 @@ function getMessageFromId({ defaultMessage: 'Use only one of kql= or lucene=, not both', }); break; + case 'tooManyFirstArguments': + message = i18n.translate('xpack.lens.indexPattern.formulaOperationTooManyFirstArguments', { + defaultMessage: + 'The operation {operation} in the Formula requires a {supported, plural, one {single} other {supported}} {type}, found: {text}', + values: { + operation: out.operation, + text: out.text, + type: out.type, + supported: out.supported || 1, + }, + }); + break; + case 'wrongArgument': + message = i18n.translate('xpack.lens.indexPattern.formulaOperationwrongArgument', { + defaultMessage: + 'The operation {operation} in the Formula does not support {type} parameters, found: {text}', + values: { operation: out.operation, text: out.text, type: out.type }, + }); + break; // case 'mathRequiresFunction': // message = i18n.translate('xpack.lens.indexPattern.formulaMathRequiresFunctionLabel', { // defaultMessage; 'The function {name} requires an Elasticsearch function', @@ -531,14 +563,16 @@ function runFullASTValidation( } else { if (nodeOperation.input === 'field') { if (shouldHaveFieldArgument(node)) { - if (!isFirstArgumentValidType(firstArg, 'variable')) { + if (!isArgumentValidType(firstArg, 'variable')) { if (isMathNode(firstArg)) { errors.push( getMessageFromId({ messageId: 'wrongFirstArgument', values: { operation: node.name, - type: 'field', + type: i18n.translate('xpack.lens.indexPattern.formulaFieldValue', { + defaultMessage: 'field', + }), argument: `math operation`, }, locations: node.location ? [node.location] : [], @@ -550,7 +584,9 @@ function runFullASTValidation( messageId: 'wrongFirstArgument', values: { operation: node.name, - type: 'field', + type: i18n.translate('xpack.lens.indexPattern.formulaFieldValue', { + defaultMessage: 'field', + }), argument: getValueOrName(firstArg) || i18n.translate('xpack.lens.indexPattern.formulaNoFieldForOperation', { @@ -561,6 +597,25 @@ function runFullASTValidation( }) ); } + } else { + // If the first argument is valid proceed with the other arguments validation + const fieldErrors = validateFieldArguments(node, variables, { + isFieldOperation: true, + firstArg, + }); + if (fieldErrors.length) { + errors.push(...fieldErrors); + } + } + const functionErrors = validateFunctionArguments(node, functions, 0, { + isFieldOperation: true, + type: i18n.translate('xpack.lens.indexPattern.formulaFieldValue', { + defaultMessage: 'field', + }), + firstArgValidation: false, + }); + if (functionErrors.length) { + errors.push(...functionErrors); } } else { // Named arguments only @@ -602,16 +657,20 @@ function runFullASTValidation( if (nodeOperation.input === 'fullReference') { // What about fn(7 + 1)? We may want to allow that // In general this should be handled down the Esaggs route rather than here - if ( - !isFirstArgumentValidType(firstArg, 'function') || - (isMathNode(firstArg) && validateMathNodes(firstArg, missingVariablesSet).length) - ) { + const isFirstArgumentNotValid = Boolean( + !isArgumentValidType(firstArg, 'function') || + (isMathNode(firstArg) && validateMathNodes(firstArg, missingVariablesSet).length) + ); + // First field has a special handling + if (isFirstArgumentNotValid) { errors.push( getMessageFromId({ messageId: 'wrongFirstArgument', values: { operation: node.name, - type: 'operation', + type: i18n.translate('xpack.lens.indexPattern.formulaOperationValue', { + defaultMessage: 'operation', + }), argument: getValueOrName(firstArg) || i18n.translate('xpack.lens.indexPattern.formulaNoOperation', { @@ -622,6 +681,21 @@ function runFullASTValidation( }) ); } + // Check for multiple function passed + const requiredFunctions = nodeOperation.requiredReferences + ? nodeOperation.requiredReferences.length + : 1; + const functionErrors = validateFunctionArguments(node, functions, requiredFunctions, { + isFieldOperation: false, + firstArgValidation: isFirstArgumentNotValid, + type: i18n.translate('xpack.lens.indexPattern.formulaMetricValue', { + defaultMessage: 'metric', + }), + }); + if (functionErrors.length) { + errors.push(...functionErrors); + } + if (!canHaveParams(nodeOperation) && namedArguments.length) { errors.push( getMessageFromId({ @@ -633,6 +707,14 @@ function runFullASTValidation( }) ); } else { + // check for fields passed at any position + const fieldErrors = validateFieldArguments(node, variables, { + isFieldOperation: false, + firstArg, + }); + if (fieldErrors.length) { + errors.push(...fieldErrors); + } const argumentsErrors = validateNameArguments( node, nodeOperation, @@ -736,7 +818,7 @@ export function hasFunctionFieldArgument(type: string) { return !['count'].includes(type); } -export function isFirstArgumentValidType(arg: TinymathAST, type: TinymathNodeTypes['type']) { +export function isArgumentValidType(arg: TinymathAST | string, type: TinymathNodeTypes['type']) { return isObject(arg) && arg.type === type; } @@ -812,3 +894,109 @@ export function validateMathNodes(root: TinymathAST, missingVariableSet: Set, + { isFieldOperation, firstArg }: { isFieldOperation: boolean; firstArg: TinymathAST } +) { + const fields = variables.filter( + (arg) => isArgumentValidType(arg, 'variable') && !isMathNode(arg) + ); + const errors = []; + if (isFieldOperation && (fields.length > 1 || (fields.length === 1 && fields[0] !== firstArg))) { + errors.push( + getMessageFromId({ + messageId: 'tooManyFirstArguments', + values: { + operation: node.name, + type: i18n.translate('xpack.lens.indexPattern.formulaFieldValue', { + defaultMessage: 'field', + }), + supported: 1, + text: (fields as TinymathVariable[]).map(({ text }) => text).join(', '), + }, + locations: node.location ? [node.location] : [], + }) + ); + } + if (!isFieldOperation && fields.length) { + errors.push( + getMessageFromId({ + messageId: 'wrongArgument', + values: { + operation: node.name, + text: (fields as TinymathVariable[]).map(({ text }) => text).join(', '), + type: i18n.translate('xpack.lens.indexPattern.formulaFieldValue', { + defaultMessage: 'field', + }), + }, + locations: node.location ? [node.location] : [], + }) + ); + } + return errors; +} + +function validateFunctionArguments( + node: TinymathFunction, + functions: TinymathFunction[], + requiredFunctions: number = 0, + { + isFieldOperation, + firstArgValidation, + type, + }: { isFieldOperation: boolean; firstArgValidation: boolean; type: string } +) { + const errors = []; + // For math operation let the native operation run its own validation + const [esOperations, mathOperations] = partition(functions, (arg) => !isMathNode(arg)); + if (esOperations.length > requiredFunctions) { + if (isFieldOperation) { + errors.push( + getMessageFromId({ + messageId: 'wrongArgument', + values: { + operation: node.name, + text: (esOperations as TinymathFunction[]).map(({ text }) => text).join(', '), + type: i18n.translate('xpack.lens.indexPattern.formulaMetricValue', { + defaultMessage: 'metric', + }), + }, + locations: node.location ? [node.location] : [], + }) + ); + } else { + errors.push( + getMessageFromId({ + messageId: 'tooManyFirstArguments', + values: { + operation: node.name, + type, + supported: requiredFunctions, + text: (esOperations as TinymathFunction[]).map(({ text }) => text).join(', '), + }, + locations: node.location ? [node.location] : [], + }) + ); + } + } + // full reference operation have another way to handle math operations + if ( + isFieldOperation && + ((!firstArgValidation && mathOperations.length) || mathOperations.length > 1) + ) { + errors.push( + getMessageFromId({ + messageId: 'wrongArgument', + values: { + operation: node.name, + type, + text: (mathOperations as TinymathFunction[]).map(({ text }) => text).join(', '), + }, + locations: node.location ? [node.location] : [], + }) + ); + } + return errors; +} diff --git a/x-pack/plugins/lens/server/routes/field_stats.ts b/x-pack/plugins/lens/server/routes/field_stats.ts index 12d3ef3f4a95e..7103e395eabdc 100644 --- a/x-pack/plugins/lens/server/routes/field_stats.ts +++ b/x-pack/plugins/lens/server/routes/field_stats.ts @@ -8,7 +8,7 @@ import { errors, estypes } from '@elastic/elasticsearch'; import DateMath from '@elastic/datemath'; import { schema } from '@kbn/config-schema'; import { CoreSetup } from 'src/core/server'; -import { IFieldType } from 'src/plugins/data/common'; +import type { IndexPatternField } from 'src/plugins/data/common'; import { SavedObjectNotFound } from '../../../../../src/plugins/kibana_utils/common'; import { ESSearchResponse } from '../../../../../src/core/types/elasticsearch'; import { FieldStatsResponse, BASE_API_URL } from '../../common'; @@ -79,6 +79,14 @@ export async function initFieldsRoute(setup: CoreSetup) { }, }; + const runtimeMappings = indexPattern.fields + .filter((f) => f.runtimeField) + .reduce((acc, f) => { + if (!f.runtimeField) return acc; + acc[f.name] = f.runtimeField; + return acc; + }, {} as Record); + const search = async (aggs: Record) => { const { body: result } = await requestClient.search({ index: indexPattern.title, @@ -86,7 +94,7 @@ export async function initFieldsRoute(setup: CoreSetup) { body: { query, aggs, - runtime_mappings: field.runtimeField ? { [fieldName]: field.runtimeField } : {}, + runtime_mappings: runtimeMappings, }, size: 0, }); @@ -138,7 +146,7 @@ export async function getNumberHistogram( aggSearchWithBody: ( aggs: Record ) => Promise, - field: IFieldType, + field: IndexPatternField, useTopHits = true ): Promise { const fieldRef = getFieldRef(field); @@ -247,7 +255,7 @@ export async function getNumberHistogram( export async function getStringSamples( aggSearchWithBody: (aggs: Record) => unknown, - field: IFieldType, + field: IndexPatternField, size = 10 ): Promise { const fieldRef = getFieldRef(field); @@ -287,7 +295,7 @@ export async function getStringSamples( // This one is not sampled so that it returns the full date range export async function getDateHistogram( aggSearchWithBody: (aggs: Record) => unknown, - field: IFieldType, + field: IndexPatternField, range: { fromDate: string; toDate: string } ): Promise { const fromDate = DateMath.parse(range.fromDate); @@ -329,7 +337,7 @@ export async function getDateHistogram( }; } -function getFieldRef(field: IFieldType) { +function getFieldRef(field: IndexPatternField) { return field.scripted ? { script: { diff --git a/x-pack/plugins/lists/public/exceptions/components/autocomplete/field.tsx b/x-pack/plugins/lists/public/exceptions/components/autocomplete/field.tsx index b3a5e36f12e40..47527914e71ff 100644 --- a/x-pack/plugins/lists/public/exceptions/components/autocomplete/field.tsx +++ b/x-pack/plugins/lists/public/exceptions/components/autocomplete/field.tsx @@ -28,6 +28,13 @@ interface OperatorProps { selectedField: IFieldType | undefined; } +/** + * There is a copy within: + * x-pack/plugins/security_solution/public/common/components/autocomplete/field.tsx + * + * TODO: This should be in its own packaged and not copied, https://github.com/elastic/kibana/issues/105378 + * NOTE: This has deviated from the copy and will have to be reconciled. + */ export const FieldComponent: React.FC = ({ fieldInputWidth, fieldTypeFilter = [], diff --git a/x-pack/plugins/lists/public/exceptions/components/autocomplete/field_value_match.tsx b/x-pack/plugins/lists/public/exceptions/components/autocomplete/field_value_match.tsx index c1776280842c6..8dbe8f223ae5b 100644 --- a/x-pack/plugins/lists/public/exceptions/components/autocomplete/field_value_match.tsx +++ b/x-pack/plugins/lists/public/exceptions/components/autocomplete/field_value_match.tsx @@ -47,6 +47,11 @@ interface AutocompleteFieldMatchProps { onError?: (arg: boolean) => void; } +/** + * There is a copy of this within: + * x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_match.tsx + * TODO: This should be in its own packaged and not copied, https://github.com/elastic/kibana/issues/105378 + */ export const AutocompleteFieldMatchComponent: React.FC = ({ placeholder, rowLabel, diff --git a/x-pack/plugins/lists/public/exceptions/components/autocomplete/helpers.ts b/x-pack/plugins/lists/public/exceptions/components/autocomplete/helpers.ts index 965214815eedf..975416e272227 100644 --- a/x-pack/plugins/lists/public/exceptions/components/autocomplete/helpers.ts +++ b/x-pack/plugins/lists/public/exceptions/components/autocomplete/helpers.ts @@ -10,6 +10,7 @@ import { EuiComboBoxOptionOption } from '@elastic/eui'; import type { ListSchema, Type } from '@kbn/securitysolution-io-ts-list-types'; import { EXCEPTION_OPERATORS, + OperatorOption, doesNotExistOperator, existsOperator, isNotOperator, @@ -18,7 +19,7 @@ import { import { IFieldType } from '../../../../../../../src/plugins/data/common'; -import { GetGenericComboBoxPropsReturn, OperatorOption } from './types'; +import { GetGenericComboBoxPropsReturn } from './types'; import * as i18n from './translations'; /** @@ -72,6 +73,10 @@ export const checkEmptyValue = ( /** * Very basic validation for values + * There is a copy within: + * x-pack/plugins/security_solution/public/common/components/autocomplete/helpers.ts + * + * TODO: This should be in its own packaged and not copied, https://github.com/elastic/kibana/issues/105378 * * @param param the value being checked * @param field the selected field @@ -109,7 +114,10 @@ export const paramIsValid = ( /** * Determines the options, selected values and option labels for EUI combo box + * There is a copy within: + * x-pack/plugins/security_solution/public/common/components/autocomplete/helpers.ts * + * TODO: This should be in its own packaged and not copied, https://github.com/elastic/kibana/issues/105378 * @param options options user can select from * @param selectedOptions user selection if any * @param getLabel helper function to know which property to use for labels diff --git a/x-pack/plugins/lists/public/exceptions/components/autocomplete/hooks/use_field_value_autocomplete.ts b/x-pack/plugins/lists/public/exceptions/components/autocomplete/hooks/use_field_value_autocomplete.ts index 674bb5e5537d9..63d3925d6d64d 100644 --- a/x-pack/plugins/lists/public/exceptions/components/autocomplete/hooks/use_field_value_autocomplete.ts +++ b/x-pack/plugins/lists/public/exceptions/components/autocomplete/hooks/use_field_value_autocomplete.ts @@ -33,7 +33,10 @@ export interface UseFieldValueAutocompleteProps { } /** * Hook for using the field value autocomplete service + * There is a copy within: + * x-pack/plugins/security_solution/public/common/components/autocomplete/hooks/use_field_value_autocomplete.ts * + * TODO: This should be in its own packaged and not copied, https://github.com/elastic/kibana/issues/105378 */ export const useFieldValueAutocomplete = ({ selectedField, diff --git a/x-pack/plugins/lists/public/exceptions/components/autocomplete/operator.tsx b/x-pack/plugins/lists/public/exceptions/components/autocomplete/operator.tsx index 7fc221c5a097c..0d2fe5bd664be 100644 --- a/x-pack/plugins/lists/public/exceptions/components/autocomplete/operator.tsx +++ b/x-pack/plugins/lists/public/exceptions/components/autocomplete/operator.tsx @@ -7,11 +7,12 @@ import React, { useCallback, useMemo } from 'react'; import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; +import { OperatorOption } from '@kbn/securitysolution-list-utils'; import { IFieldType } from '../../../../../../../src/plugins/data/common'; import { getGenericComboBoxProps, getOperators } from './helpers'; -import { GetGenericComboBoxPropsReturn, OperatorOption } from './types'; +import { GetGenericComboBoxPropsReturn } from './types'; const AS_PLAIN_TEXT = { asPlainText: true }; diff --git a/x-pack/plugins/lists/public/exceptions/components/autocomplete/types.ts b/x-pack/plugins/lists/public/exceptions/components/autocomplete/types.ts index 76d5b7758007b..07f1903fb70e1 100644 --- a/x-pack/plugins/lists/public/exceptions/components/autocomplete/types.ts +++ b/x-pack/plugins/lists/public/exceptions/components/autocomplete/types.ts @@ -6,20 +6,9 @@ */ import { EuiComboBoxOptionOption } from '@elastic/eui'; -import type { - ListOperatorEnum as OperatorEnum, - ListOperatorTypeEnum as OperatorTypeEnum, -} from '@kbn/securitysolution-io-ts-list-types'; export interface GetGenericComboBoxPropsReturn { comboOptions: EuiComboBoxOptionOption[]; labels: string[]; selectedComboOptions: EuiComboBoxOptionOption[]; } - -export interface OperatorOption { - message: string; - value: string; - operator: OperatorEnum; - type: OperatorTypeEnum; -} diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.tsx b/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.tsx index 7daef8467dd1a..c54da89766d76 100644 --- a/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.tsx +++ b/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.tsx @@ -18,6 +18,7 @@ import { BuilderEntry, EXCEPTION_OPERATORS_ONLY_LISTS, FormattedBuilderEntry, + OperatorOption, getEntryOnFieldChange, getEntryOnListChange, getEntryOnMatchAnyChange, @@ -32,7 +33,6 @@ import { IFieldType, IIndexPattern } from '../../../../../../../src/plugins/data import { HttpStart } from '../../../../../../../src/core/public'; import { FieldComponent } from '../autocomplete/field'; import { OperatorComponent } from '../autocomplete/operator'; -import { OperatorOption } from '../autocomplete/types'; import { AutocompleteFieldExistsComponent } from '../autocomplete/field_value_exists'; import { AutocompleteFieldMatchComponent } from '../autocomplete/field_value_match'; import { AutocompleteFieldMatchAnyComponent } from '../autocomplete/field_value_match_any'; diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/helpers.test.ts b/x-pack/plugins/lists/public/exceptions/components/builder/helpers.test.ts index 212db40f3168c..afeac2d1bf4de 100644 --- a/x-pack/plugins/lists/public/exceptions/components/builder/helpers.test.ts +++ b/x-pack/plugins/lists/public/exceptions/components/builder/helpers.test.ts @@ -24,6 +24,7 @@ import { EmptyEntry, ExceptionsBuilderExceptionItem, FormattedBuilderEntry, + OperatorOption, doesNotExistOperator, existsOperator, filterExceptionItems, @@ -64,7 +65,6 @@ import { getEntryNestedMock } from '../../../../common/schemas/types/entry_neste import { getEntryMatchMock } from '../../../../common/schemas/types/entry_match.mock'; import { getEntryMatchAnyMock } from '../../../../common/schemas/types/entry_match_any.mock'; import { getListResponseMock } from '../../../../common/schemas/response/list_schema.mock'; -import { OperatorOption } from '../autocomplete/types'; import { getEntryListMock } from '../../../../common/schemas/types/entry_list.mock'; // TODO: ALL THESE TESTS SHOULD BE MOVED TO @kbn/securitysolution-list-utils for its helper. The only reason why they're here is due to missing other packages we hae to create or missing things from kbn packages such as mocks from kibana core diff --git a/x-pack/plugins/maps/public/actions/data_request_actions.ts b/x-pack/plugins/maps/public/actions/data_request_actions.ts index 47350474eef04..50df95a52c4d4 100644 --- a/x-pack/plugins/maps/public/actions/data_request_actions.ts +++ b/x-pack/plugins/maps/public/actions/data_request_actions.ts @@ -55,6 +55,7 @@ export type DataRequestContext = { startLoading(dataId: string, requestToken: symbol, requestMeta?: DataMeta): void; stopLoading(dataId: string, requestToken: symbol, data: object, resultsMeta?: DataMeta): void; onLoadError(dataId: string, requestToken: symbol, errorMessage: string): void; + onJoinError(errorMessage: string): void; updateSourceData(newData: unknown): void; isRequestStillActive(dataId: string, requestToken: symbol): boolean; registerCancelCallback(requestToken: symbol, callback: () => void): void; @@ -121,6 +122,8 @@ function getDataRequestContext( dispatch(endDataLoad(layerId, dataId, requestToken, data, meta)), onLoadError: (dataId: string, requestToken: symbol, errorMessage: string) => dispatch(onDataLoadError(layerId, dataId, requestToken, errorMessage)), + onJoinError: (errorMessage: string) => + dispatch(setLayerDataLoadErrorStatus(layerId, errorMessage)), updateSourceData: (newData: object) => { dispatch(updateSourceDataRequest(layerId, newData)); }, @@ -193,13 +196,11 @@ export function syncDataForLayerId(layerId: string | null) { } function setLayerDataLoadErrorStatus(layerId: string, errorMessage: string | null) { - return (dispatch: Dispatch) => { - dispatch({ - type: SET_LAYER_ERROR_STATUS, - isInErrorState: errorMessage !== null, - layerId, - errorMessage, - }); + return { + type: SET_LAYER_ERROR_STATUS, + isInErrorState: errorMessage !== null, + layerId, + errorMessage, }; } diff --git a/x-pack/plugins/maps/public/classes/joins/inner_join.ts b/x-pack/plugins/maps/public/classes/joins/inner_join.ts index 988690233d484..9f6fec576e9e2 100644 --- a/x-pack/plugins/maps/public/classes/joins/inner_join.ts +++ b/x-pack/plugins/maps/public/classes/joins/inner_join.ts @@ -118,17 +118,23 @@ export class InnerJoin { }); } - const joinKey = feature.properties[this._leftField.getName()]; - const coercedKey = - typeof joinKey === 'undefined' || joinKey === null ? null : joinKey.toString(); - if (coercedKey !== null && propertiesMap.has(coercedKey)) { - Object.assign(feature.properties, propertiesMap.get(coercedKey)); + const joinKey = this.getJoinKey(feature); + if (joinKey !== null && propertiesMap.has(joinKey)) { + Object.assign(feature.properties, propertiesMap.get(joinKey)); return true; } else { return false; } } + getJoinKey(feature: Feature): string | null { + const joinKey = + feature.properties && this._leftField + ? feature.properties[this._leftField.getName()] + : undefined; + return joinKey === undefined || joinKey === null ? null : joinKey.toString(); + } + getRightJoinSource(): ITermJoinSource { if (!this._rightSource) { throw new Error('Cannot get rightSource from InnerJoin with incomplete config'); diff --git a/x-pack/plugins/maps/public/classes/layers/__fixtures__/mock_sync_context.ts b/x-pack/plugins/maps/public/classes/layers/__fixtures__/mock_sync_context.ts index dd1367605376d..16aca6760c4d5 100644 --- a/x-pack/plugins/maps/public/classes/layers/__fixtures__/mock_sync_context.ts +++ b/x-pack/plugins/maps/public/classes/layers/__fixtures__/mock_sync_context.ts @@ -16,6 +16,7 @@ export class MockSyncContext implements DataRequestContext { registerCancelCallback: (requestToken: symbol, callback: () => void) => void; startLoading: (dataId: string, requestToken: symbol, meta: DataMeta) => void; stopLoading: (dataId: string, requestToken: symbol, data: object, meta: DataMeta) => void; + onJoinError: (errorMessage: string) => void; updateSourceData: (newData: unknown) => void; forceRefresh: boolean; @@ -38,6 +39,7 @@ export class MockSyncContext implements DataRequestContext { this.registerCancelCallback = sinon.spy(); this.startLoading = sinon.spy(); this.stopLoading = sinon.spy(); + this.onJoinError = sinon.spy(); this.updateSourceData = sinon.spy(); this.forceRefresh = false; } diff --git a/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/wizard.tsx b/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/wizard.tsx index 024c2308df6c6..87747d915af4a 100644 --- a/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/wizard.tsx +++ b/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/wizard.tsx @@ -106,7 +106,7 @@ export class ClientFileCreateSourceEditor extends Component { applyGlobalTime: true, id: '12345', indexPatternId: 'apm_static_index_pattern_id', - indexPatternTitle: 'apm-*', + indexPatternTitle: 'traces-apm*,logs-apm*,metrics-apm*,apm-*', metrics: [ { field: 'transaction.duration.us', diff --git a/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/create_layer_descriptor.ts b/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/create_layer_descriptor.ts index adf6f1d7f270d..0b57afb38d585 100644 --- a/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/create_layer_descriptor.ts +++ b/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/create_layer_descriptor.ts @@ -39,7 +39,7 @@ import { getDefaultDynamicProperties } from '../../../styles/vector/vector_style // redefining APM constant to avoid making maps app depend on APM plugin export const APM_INDEX_PATTERN_ID = 'apm_static_index_pattern_id'; -export const APM_INDEX_PATTERN_TITLE = 'apm-*'; +export const APM_INDEX_PATTERN_TITLE = 'traces-apm*,logs-apm*,metrics-apm*,apm-*'; const defaultDynamicProperties = getDefaultDynamicProperties(); diff --git a/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.test.ts b/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.test.ts index 9c6e72fc11d3a..a3a3e8b20f678 100644 --- a/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.test.ts +++ b/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.test.ts @@ -706,4 +706,343 @@ describe('createLayerDescriptor', () => { }, ]); }); + + test('apm data stream', () => { + expect(createSecurityLayerDescriptors('id', 'traces-apm-opbean-node')).toEqual([ + { + __dataRequests: [], + alpha: 0.75, + id: '12345', + includeInFitToBounds: true, + joins: [], + label: 'traces-apm-opbean-node | Source Point', + maxZoom: 24, + minZoom: 0, + sourceDescriptor: { + applyGlobalQuery: true, + applyGlobalTime: true, + filterByMapBounds: true, + geoField: 'client.geo.location', + id: '12345', + indexPatternId: 'id', + scalingType: 'TOP_HITS', + sortField: '', + sortOrder: 'desc', + tooltipProperties: [ + 'host.name', + 'client.ip', + 'client.domain', + 'client.geo.country_iso_code', + 'client.as.organization.name', + ], + topHitsSize: 1, + topHitsSplitField: 'client.ip', + type: 'ES_SEARCH', + }, + style: { + isTimeAware: true, + properties: { + fillColor: { + options: { + color: '#6092C0', + }, + type: 'STATIC', + }, + icon: { + options: { + value: 'home', + }, + type: 'STATIC', + }, + iconOrientation: { + options: { + orientation: 0, + }, + type: 'STATIC', + }, + iconSize: { + options: { + size: 8, + }, + type: 'STATIC', + }, + labelBorderColor: { + options: { + color: '#FFFFFF', + }, + type: 'STATIC', + }, + labelBorderSize: { + options: { + size: 'SMALL', + }, + }, + labelColor: { + options: { + color: '#000000', + }, + type: 'STATIC', + }, + labelSize: { + options: { + size: 14, + }, + type: 'STATIC', + }, + labelText: { + options: { + value: '', + }, + type: 'STATIC', + }, + lineColor: { + options: { + color: '#FFFFFF', + }, + type: 'STATIC', + }, + lineWidth: { + options: { + size: 2, + }, + type: 'STATIC', + }, + symbolizeAs: { + options: { + value: 'icon', + }, + }, + }, + type: 'VECTOR', + }, + type: 'VECTOR', + visible: true, + }, + { + __dataRequests: [], + alpha: 0.75, + id: '12345', + includeInFitToBounds: true, + joins: [], + label: 'traces-apm-opbean-node | Destination point', + maxZoom: 24, + minZoom: 0, + sourceDescriptor: { + applyGlobalQuery: true, + applyGlobalTime: true, + filterByMapBounds: true, + geoField: 'server.geo.location', + id: '12345', + indexPatternId: 'id', + scalingType: 'TOP_HITS', + sortField: '', + sortOrder: 'desc', + tooltipProperties: [ + 'host.name', + 'server.ip', + 'server.domain', + 'server.geo.country_iso_code', + 'server.as.organization.name', + ], + topHitsSize: 1, + topHitsSplitField: 'server.ip', + type: 'ES_SEARCH', + }, + style: { + isTimeAware: true, + properties: { + fillColor: { + options: { + color: '#D36086', + }, + type: 'STATIC', + }, + icon: { + options: { + value: 'marker', + }, + type: 'STATIC', + }, + iconOrientation: { + options: { + orientation: 0, + }, + type: 'STATIC', + }, + iconSize: { + options: { + size: 8, + }, + type: 'STATIC', + }, + labelBorderColor: { + options: { + color: '#FFFFFF', + }, + type: 'STATIC', + }, + labelBorderSize: { + options: { + size: 'SMALL', + }, + }, + labelColor: { + options: { + color: '#000000', + }, + type: 'STATIC', + }, + labelSize: { + options: { + size: 14, + }, + type: 'STATIC', + }, + labelText: { + options: { + value: '', + }, + type: 'STATIC', + }, + lineColor: { + options: { + color: '#FFFFFF', + }, + type: 'STATIC', + }, + lineWidth: { + options: { + size: 2, + }, + type: 'STATIC', + }, + symbolizeAs: { + options: { + value: 'icon', + }, + }, + }, + type: 'VECTOR', + }, + type: 'VECTOR', + visible: true, + }, + { + __dataRequests: [], + alpha: 0.75, + id: '12345', + includeInFitToBounds: true, + joins: [], + label: 'traces-apm-opbean-node | Line', + maxZoom: 24, + minZoom: 0, + sourceDescriptor: { + applyGlobalQuery: true, + applyGlobalTime: true, + destGeoField: 'server.geo.location', + id: '12345', + indexPatternId: 'id', + metrics: [ + { + field: 'client.bytes', + type: 'sum', + }, + { + field: 'server.bytes', + type: 'sum', + }, + ], + sourceGeoField: 'client.geo.location', + type: 'ES_PEW_PEW', + }, + style: { + isTimeAware: true, + properties: { + fillColor: { + options: { + color: '#54B399', + }, + type: 'STATIC', + }, + icon: { + options: { + value: 'marker', + }, + type: 'STATIC', + }, + iconOrientation: { + options: { + orientation: 0, + }, + type: 'STATIC', + }, + iconSize: { + options: { + size: 6, + }, + type: 'STATIC', + }, + labelBorderColor: { + options: { + color: '#FFFFFF', + }, + type: 'STATIC', + }, + labelBorderSize: { + options: { + size: 'SMALL', + }, + }, + labelColor: { + options: { + color: '#000000', + }, + type: 'STATIC', + }, + labelSize: { + options: { + size: 14, + }, + type: 'STATIC', + }, + labelText: { + options: { + value: '', + }, + type: 'STATIC', + }, + lineColor: { + options: { + color: '#6092C0', + }, + type: 'STATIC', + }, + lineWidth: { + options: { + field: { + name: 'doc_count', + origin: 'source', + }, + fieldMetaOptions: { + isEnabled: true, + sigma: 3, + }, + maxSize: 8, + minSize: 1, + }, + type: 'DYNAMIC', + }, + symbolizeAs: { + options: { + value: 'circle', + }, + }, + }, + type: 'VECTOR', + }, + type: 'VECTOR', + visible: true, + }, + ]); + }); }); diff --git a/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.ts b/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.ts index b2283196a41dd..8a40ba63bed0d 100644 --- a/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.ts +++ b/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.ts @@ -35,7 +35,11 @@ const defaultDynamicProperties = getDefaultDynamicProperties(); const euiVisColorPalette = euiPaletteColorBlind(); function isApmIndex(indexPatternTitle: string) { - return minimatch(indexPatternTitle, APM_INDEX_PATTERN_TITLE); + return APM_INDEX_PATTERN_TITLE.split(',') + .map((pattern) => { + return minimatch(indexPatternTitle, pattern); + }) + .some(Boolean); } function getSourceField(indexPatternTitle: string) { diff --git a/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx index 880a47d0981cb..6277411fa053a 100644 --- a/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx @@ -95,13 +95,13 @@ export class TiledVectorLayer extends VectorLayer { ? i18n.translate('xpack.maps.tiles.resultsTrimmedMsg', { defaultMessage: `Results limited to {count} documents.`, values: { - count: totalFeaturesCount, + count: totalFeaturesCount.toLocaleString(), }, }) : i18n.translate('xpack.maps.tiles.resultsCompleteMsg', { defaultMessage: `Found {count} documents.`, values: { - count: totalFeaturesCount, + count: totalFeaturesCount.toLocaleString(), }, }), areResultsTrimmed: isIncomplete, diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/perform_inner_joins.test.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/perform_inner_joins.test.ts new file mode 100644 index 0000000000000..aad1b693be79b --- /dev/null +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/perform_inner_joins.test.ts @@ -0,0 +1,285 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import sinon from 'sinon'; +import _ from 'lodash'; +import { FeatureCollection } from 'geojson'; +import { ESTermSourceDescriptor } from '../../../../common/descriptor_types'; +import { + AGG_TYPE, + FEATURE_VISIBLE_PROPERTY_NAME, + SOURCE_TYPES, +} from '../../../../common/constants'; +import { performInnerJoins } from './perform_inner_joins'; +import { InnerJoin } from '../../joins/inner_join'; +import { IVectorSource } from '../../sources/vector_source'; +import { IField } from '../../fields/field'; + +const LEFT_FIELD = 'leftKey'; +const COUNT_PROPERTY_NAME = '__kbnjoin__count__d3625663-5b34-4d50-a784-0d743f676a0c'; +const featureCollection = { + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + properties: { + [FEATURE_VISIBLE_PROPERTY_NAME]: false, + [LEFT_FIELD]: 'alpha', + }, + geometry: { + type: 'Point', + coordinates: [-112, 46], + }, + }, + { + type: 'Feature', + properties: { + [COUNT_PROPERTY_NAME]: 20, + [FEATURE_VISIBLE_PROPERTY_NAME]: true, + [LEFT_FIELD]: 'bravo', + }, + geometry: { + type: 'Point', + coordinates: [-100, 40], + }, + }, + ], +}; + +const joinDescriptor = { + leftField: LEFT_FIELD, + right: { + applyGlobalQuery: true, + applyGlobalTime: true, + id: 'd3625663-5b34-4d50-a784-0d743f676a0c', + indexPatternId: 'myIndexPattern', + metrics: [ + { + type: AGG_TYPE.COUNT, + }, + ], + term: 'rightKey', + type: SOURCE_TYPES.ES_TERM_SOURCE, + } as ESTermSourceDescriptor, +}; +const mockVectorSource = ({ + getInspectorAdapters: () => { + return undefined; + }, + createField: () => { + return { + getName: () => { + return LEFT_FIELD; + }, + } as IField; + }, +} as unknown) as IVectorSource; +const innerJoin = new InnerJoin(joinDescriptor, mockVectorSource); +const propertiesMap = new Map>(); +propertiesMap.set('alpha', { [COUNT_PROPERTY_NAME]: 1 }); + +test('should skip join when no state has changed', () => { + const updateSourceData = sinon.spy(); + const onJoinError = sinon.spy(); + + performInnerJoins( + { + refreshed: false, + featureCollection: _.cloneDeep(featureCollection) as FeatureCollection, + }, + [ + { + dataHasChanged: false, + join: innerJoin, + }, + ], + updateSourceData, + onJoinError + ); + + expect(updateSourceData.notCalled); + expect(onJoinError.notCalled); +}); + +test('should perform join when features change', () => { + const updateSourceData = sinon.spy(); + const onJoinError = sinon.spy(); + + performInnerJoins( + { + refreshed: true, + featureCollection: _.cloneDeep(featureCollection) as FeatureCollection, + }, + [ + { + dataHasChanged: false, + join: innerJoin, + }, + ], + updateSourceData, + onJoinError + ); + + expect(updateSourceData.calledOnce); + expect(onJoinError.notCalled); +}); + +test('should perform join when join state changes', () => { + const updateSourceData = sinon.spy(); + const onJoinError = sinon.spy(); + + performInnerJoins( + { + refreshed: false, + featureCollection: _.cloneDeep(featureCollection) as FeatureCollection, + }, + [ + { + dataHasChanged: true, + join: innerJoin, + }, + ], + updateSourceData, + onJoinError + ); + + expect(updateSourceData.calledOnce); + expect(onJoinError.notCalled); +}); + +test('should call updateSourceData with feature collection with updated feature visibility and join properties', () => { + const updateSourceData = sinon.spy(); + const onJoinError = sinon.spy(); + + performInnerJoins( + { + refreshed: true, + featureCollection: _.cloneDeep(featureCollection) as FeatureCollection, + }, + [ + { + dataHasChanged: false, + join: innerJoin, + propertiesMap, + }, + ], + updateSourceData, + onJoinError + ); + + const firstCallArgs = updateSourceData.args[0]; + const updateSourceDataFeatureCollection = firstCallArgs[0]; + expect(updateSourceDataFeatureCollection).toEqual({ + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + properties: { + [COUNT_PROPERTY_NAME]: 1, + [FEATURE_VISIBLE_PROPERTY_NAME]: true, + [LEFT_FIELD]: 'alpha', + }, + geometry: { + type: 'Point', + coordinates: [-112, 46], + }, + }, + { + type: 'Feature', + properties: { + [FEATURE_VISIBLE_PROPERTY_NAME]: false, + [LEFT_FIELD]: 'bravo', + }, + geometry: { + type: 'Point', + coordinates: [-100, 40], + }, + }, + ], + }); + expect(onJoinError.notCalled); +}); + +test('should call updateSourceData when no results returned from terms aggregation', () => { + const updateSourceData = sinon.spy(); + const onJoinError = sinon.spy(); + + performInnerJoins( + { + refreshed: false, + featureCollection: _.cloneDeep(featureCollection) as FeatureCollection, + }, + [ + { + dataHasChanged: true, + join: innerJoin, + }, + ], + updateSourceData, + onJoinError + ); + + const firstCallArgs = updateSourceData.args[0]; + const updateSourceDataFeatureCollection = firstCallArgs[0]; + expect(updateSourceDataFeatureCollection).toEqual({ + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + properties: { + [FEATURE_VISIBLE_PROPERTY_NAME]: false, + [LEFT_FIELD]: 'alpha', + }, + geometry: { + type: 'Point', + coordinates: [-112, 46], + }, + }, + { + type: 'Feature', + properties: { + [COUNT_PROPERTY_NAME]: 20, + [FEATURE_VISIBLE_PROPERTY_NAME]: false, + [LEFT_FIELD]: 'bravo', + }, + geometry: { + type: 'Point', + coordinates: [-100, 40], + }, + }, + ], + }); + expect(onJoinError.notCalled); +}); + +test('should call onJoinError when there are no matching features', () => { + const updateSourceData = sinon.spy(); + const onJoinError = sinon.spy(); + + // instead of returning military alphabet like "alpha" or "bravo", mismatched key returns numbers, like '1' + const propertiesMapFromMismatchedKey = new Map>(); + propertiesMapFromMismatchedKey.set('1', { [COUNT_PROPERTY_NAME]: 1 }); + + performInnerJoins( + { + refreshed: false, + featureCollection: _.cloneDeep(featureCollection) as FeatureCollection, + }, + [ + { + dataHasChanged: true, + join: innerJoin, + propertiesMap: propertiesMapFromMismatchedKey, + }, + ], + updateSourceData, + onJoinError + ); + + expect(updateSourceData.notCalled); + expect(onJoinError.calledOnce); +}); diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/perform_inner_joins.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/perform_inner_joins.ts new file mode 100644 index 0000000000000..23c6527d3e818 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/perform_inner_joins.ts @@ -0,0 +1,134 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FeatureCollection } from 'geojson'; +import { i18n } from '@kbn/i18n'; +import { FEATURE_VISIBLE_PROPERTY_NAME } from '../../../../common/constants'; +import { DataRequestContext } from '../../../actions'; +import { InnerJoin } from '../../joins/inner_join'; +import { PropertiesMap } from '../../../../common/elasticsearch_util'; + +interface SourceResult { + refreshed: boolean; + featureCollection: FeatureCollection; +} + +export interface JoinState { + dataHasChanged: boolean; + join: InnerJoin; + propertiesMap?: PropertiesMap; +} + +export function performInnerJoins( + sourceResult: SourceResult, + joinStates: JoinState[], + updateSourceData: DataRequestContext['updateSourceData'], + onJoinError: DataRequestContext['onJoinError'] +) { + // should update the store if + // -- source result was refreshed + // -- any of the join configurations changed (joinState changed) + // -- visibility of any of the features has changed + + let shouldUpdateStore = + sourceResult.refreshed || joinStates.some((joinState) => joinState.dataHasChanged); + + if (!shouldUpdateStore) { + return; + } + + const joinStatuses = joinStates.map((joinState) => { + return { + joinedWithAtLeastOneFeature: false, + keys: [] as string[], + joinState, + }; + }); + + for (let i = 0; i < sourceResult.featureCollection.features.length; i++) { + const feature = sourceResult.featureCollection.features[i]; + if (!feature.properties) { + feature.properties = {}; + } + const oldVisbility = feature.properties[FEATURE_VISIBLE_PROPERTY_NAME]; + let isFeatureVisible = true; + for (let j = 0; j < joinStates.length; j++) { + const joinState = joinStates[j]; + const innerJoin = joinState.join; + const joinStatus = joinStatuses[j]; + const joinKey = innerJoin.getJoinKey(feature); + if (joinKey !== null) { + joinStatus.keys.push(joinKey); + } + const canJoinOnCurrent = joinState.propertiesMap + ? innerJoin.joinPropertiesToFeature(feature, joinState.propertiesMap) + : false; + if (canJoinOnCurrent && !joinStatus.joinedWithAtLeastOneFeature) { + joinStatus.joinedWithAtLeastOneFeature = true; + } + isFeatureVisible = isFeatureVisible && canJoinOnCurrent; + } + + if (oldVisbility !== isFeatureVisible) { + shouldUpdateStore = true; + } + + feature.properties[FEATURE_VISIBLE_PROPERTY_NAME] = isFeatureVisible; + } + + if (shouldUpdateStore) { + updateSourceData({ ...sourceResult.featureCollection }); + } + + const joinStatusesWithoutAnyMatches = joinStatuses.filter((joinStatus) => { + return ( + !joinStatus.joinedWithAtLeastOneFeature && joinStatus.joinState.propertiesMap !== undefined + ); + }); + + if (joinStatusesWithoutAnyMatches.length) { + function prettyPrintArray(array: unknown[]) { + return array.length <= 5 + ? array.join(',') + : array.slice(0, 5).join(',') + + i18n.translate('xpack.maps.vectorLayer.joinError.firstTenMsg', { + defaultMessage: ` (5 of {total})`, + values: { total: array.length }, + }); + } + + const joinStatus = joinStatusesWithoutAnyMatches[0]; + const leftFieldName = joinStatus.joinState.join.getLeftField().getName(); + const reason = + joinStatus.keys.length === 0 + ? i18n.translate('xpack.maps.vectorLayer.joinError.noLeftFieldValuesMsg', { + defaultMessage: `Left field: '{leftFieldName}', does not provide any values.`, + values: { leftFieldName }, + }) + : i18n.translate('xpack.maps.vectorLayer.joinError.noMatchesMsg', { + defaultMessage: `Left field does not match right field. Left field: '{leftFieldName}' has values { leftFieldValues }. Right field: '{rightFieldName}' has values: { rightFieldValues }.`, + values: { + leftFieldName, + leftFieldValues: prettyPrintArray(joinStatus.keys), + rightFieldName: joinStatus.joinState.join + .getRightJoinSource() + .getTermField() + .getName(), + rightFieldValues: prettyPrintArray( + Array.from(joinStatus.joinState.propertiesMap!.keys()) + ), + }, + }); + + onJoinError( + i18n.translate('xpack.maps.vectorLayer.joinErrorMsg', { + defaultMessage: `Unable to perform term join. {reason}`, + values: { reason }, + }) + ); + } +} diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx index 959064b3daab2..74a0ea1e63882 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx @@ -69,17 +69,7 @@ import { IESSource } from '../../sources/es_source'; import { PropertiesMap } from '../../../../common/elasticsearch_util'; import { ITermJoinSource } from '../../sources/term_join_source'; import { addGeoJsonMbSource, getVectorSourceBounds, syncVectorSource } from './utils'; - -interface SourceResult { - refreshed: boolean; - featureCollection?: FeatureCollection; -} - -interface JoinState { - dataHasChanged: boolean; - join: InnerJoin; - propertiesMap?: PropertiesMap; -} +import { JoinState, performInnerJoins } from './perform_inner_joins'; export interface VectorLayerArguments { source: IVectorSource; @@ -435,51 +425,6 @@ export class VectorLayer extends AbstractLayer implements IVectorLayer { }; } - _performInnerJoins( - sourceResult: SourceResult, - joinStates: JoinState[], - updateSourceData: DataRequestContext['updateSourceData'] - ) { - // should update the store if - // -- source result was refreshed - // -- any of the join configurations changed (joinState changed) - // -- visibility of any of the features has changed - - let shouldUpdateStore = - sourceResult.refreshed || joinStates.some((joinState) => joinState.dataHasChanged); - - if (!shouldUpdateStore) { - return; - } - - for (let i = 0; i < sourceResult.featureCollection!.features.length; i++) { - const feature = sourceResult.featureCollection!.features[i]; - if (!feature.properties) { - feature.properties = {}; - } - const oldVisbility = feature.properties[FEATURE_VISIBLE_PROPERTY_NAME]; - let isFeatureVisible = true; - for (let j = 0; j < joinStates.length; j++) { - const joinState = joinStates[j]; - const innerJoin = joinState.join; - const canJoinOnCurrent = joinState.propertiesMap - ? innerJoin.joinPropertiesToFeature(feature, joinState.propertiesMap) - : false; - isFeatureVisible = isFeatureVisible && canJoinOnCurrent; - } - - if (oldVisbility !== isFeatureVisible) { - shouldUpdateStore = true; - } - - feature.properties[FEATURE_VISIBLE_PROPERTY_NAME] = isFeatureVisible; - } - - if (shouldUpdateStore) { - updateSourceData({ ...sourceResult.featureCollection }); - } - } - async _syncSourceStyleMeta( syncContext: DataRequestContext, source: IVectorSource, @@ -714,7 +659,12 @@ export class VectorLayer extends AbstractLayer implements IVectorLayer { } const joinStates = await this._syncJoins(syncContext, style); - this._performInnerJoins(sourceResult, joinStates, syncContext.updateSourceData); + performInnerJoins( + sourceResult, + joinStates, + syncContext.updateSourceData, + syncContext.onJoinError + ); } catch (error) { if (!(error instanceof DataRequestAbortError)) { throw error; diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_line_source/es_geo_line_source.test.ts b/x-pack/plugins/maps/public/classes/sources/es_geo_line_source/es_geo_line_source.test.ts index 6d3171312eaec..0c15afff6b051 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_line_source/es_geo_line_source.test.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_line_source/es_geo_line_source.test.ts @@ -51,7 +51,7 @@ describe('getSourceTooltipContent', () => { sourceDataRequest ); expect(areResultsTrimmed).toBe(true); - expect(tooltipContent).toBe('Results limited to first 1000 tracks of ~5000.'); + expect(tooltipContent).toBe('Results limited to first 1,000 tracks of ~5,000.'); }); it('Should show results trimmed icon and message when tracks are trimmed', () => { @@ -90,7 +90,7 @@ describe('getSourceTooltipContent', () => { ); expect(areResultsTrimmed).toBe(true); expect(tooltipContent).toBe( - 'Results limited to first 1000 tracks of ~5000. 10 of 1000 tracks are incomplete.' + 'Results limited to first 1,000 tracks of ~5,000. 10 of 1,000 tracks are incomplete.' ); }); }); diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_line_source/es_geo_line_source.tsx b/x-pack/plugins/maps/public/classes/sources/es_geo_line_source/es_geo_line_source.tsx index 460c1228e50a8..82be83dad43f7 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_line_source/es_geo_line_source.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_line_source/es_geo_line_source.tsx @@ -326,21 +326,21 @@ export class ESGeoLineSource extends AbstractESAggSource { ? i18n.translate('xpack.maps.esGeoLine.areEntitiesTrimmedMsg', { defaultMessage: `Results limited to first {entityCount} tracks of ~{totalEntities}.`, values: { - entityCount: meta.entityCount, - totalEntities: meta.totalEntities, + entityCount: meta.entityCount.toLocaleString(), + totalEntities: meta.totalEntities.toLocaleString(), }, }) : i18n.translate('xpack.maps.esGeoLine.tracksCountMsg', { defaultMessage: `Found {entityCount} tracks.`, - values: { entityCount: meta.entityCount }, + values: { entityCount: meta.entityCount.toLocaleString() }, }); const tracksTrimmedMsg = meta.numTrimmedTracks > 0 ? i18n.translate('xpack.maps.esGeoLine.tracksTrimmedMsg', { defaultMessage: `{numTrimmedTracks} of {entityCount} tracks are incomplete.`, values: { - entityCount: meta.entityCount, - numTrimmedTracks: meta.numTrimmedTracks, + entityCount: meta.entityCount.toLocaleString(), + numTrimmedTracks: meta.numTrimmedTracks.toLocaleString(), }, }) : undefined; diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx index 343c366b548f6..55eed588b8840 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx @@ -620,17 +620,17 @@ export class ESSearchSource extends AbstractESSource implements ITiledSingleLaye ? i18n.translate('xpack.maps.esSearch.topHitsResultsTrimmedMsg', { defaultMessage: `Results limited to first {entityCount} entities of ~{totalEntities}.`, values: { - entityCount: meta.entityCount, - totalEntities: meta.totalEntities, + entityCount: meta.entityCount?.toLocaleString(), + totalEntities: meta.totalEntities?.toLocaleString(), }, }) : i18n.translate('xpack.maps.esSearch.topHitsEntitiesCountMsg', { defaultMessage: `Found {entityCount} entities.`, - values: { entityCount: meta.entityCount }, + values: { entityCount: meta.entityCount?.toLocaleString() }, }); const docsPerEntityMsg = i18n.translate('xpack.maps.esSearch.topHitsSizeMsg', { defaultMessage: `Showing top {topHitsSize} documents per entity.`, - values: { topHitsSize: this._descriptor.topHitsSize }, + values: { topHitsSize: this._descriptor.topHitsSize?.toLocaleString() }, }); return { @@ -645,7 +645,7 @@ export class ESSearchSource extends AbstractESSource implements ITiledSingleLaye return { tooltipContent: i18n.translate('xpack.maps.esSearch.resultsTrimmedMsg', { defaultMessage: `Results limited to first {count} documents.`, - values: { count: meta.resultsCount }, + values: { count: meta.resultsCount?.toLocaleString() }, }), areResultsTrimmed: true, }; @@ -654,7 +654,7 @@ export class ESSearchSource extends AbstractESSource implements ITiledSingleLaye return { tooltipContent: i18n.translate('xpack.maps.esSearch.featureCountMsg', { defaultMessage: `Found {count} documents.`, - values: { count: meta.resultsCount }, + values: { count: meta.resultsCount?.toLocaleString() }, }), areResultsTrimmed: false, }; diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/util/__snapshots__/scaling_form.test.tsx.snap b/x-pack/plugins/maps/public/classes/sources/es_search_source/util/__snapshots__/scaling_form.test.tsx.snap index 03f2594f287ea..99ce13ce326d6 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/util/__snapshots__/scaling_form.test.tsx.snap +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/util/__snapshots__/scaling_form.test.tsx.snap @@ -28,7 +28,7 @@ exports[`scaling form should disable clusters option when clustering is not supp @@ -114,14 +114,14 @@ exports[`scaling form should render 1`] = ` { state = { - maxResultWindow: DEFAULT_MAX_RESULT_WINDOW, + maxResultWindow: DEFAULT_MAX_RESULT_WINDOW.toLocaleString(), }; _isMounted = false; @@ -61,7 +61,7 @@ export class ScalingForm extends Component { const indexPattern = await getIndexPatternService().get(this.props.indexPatternId); const { maxResultWindow } = await loadIndexSettings(indexPattern!.title); if (this._isMounted) { - this.setState({ maxResultWindow }); + this.setState({ maxResultWindow: maxResultWindow.toLocaleString() }); } } catch (err) { return; @@ -90,7 +90,7 @@ export class ScalingForm extends Component { { , + "name": "Edit layer settings", + "onClick": [Function], + "toolTipContent": null, + }, ], "title": "Layer actions", }, diff --git a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx index 83b4d2c2a756b..2a3186f00d7ce 100644 --- a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx +++ b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx @@ -158,6 +158,17 @@ export class TOCEntryActionsPopover extends Component { }, }, ]; + actionItems.push({ + disabled: this.props.isEditButtonDisabled, + name: EDIT_LAYER_SETTINGS_LABEL, + icon: , + 'data-test-subj': 'layerSettingsButton', + toolTipContent: null, + onClick: () => { + this._closePopover(); + this.props.openLayerSettings(); + }, + }); if (!this.props.isReadOnly) { if (this.state.supportsFeatureEditing) { @@ -186,17 +197,6 @@ export class TOCEntryActionsPopover extends Component { }, }); } - actionItems.push({ - disabled: this.props.isEditButtonDisabled, - name: EDIT_LAYER_SETTINGS_LABEL, - icon: , - 'data-test-subj': 'layerSettingsButton', - toolTipContent: null, - onClick: () => { - this._closePopover(); - this.props.openLayerSettings(); - }, - }); actionItems.push({ name: i18n.translate('xpack.maps.layerTocActions.cloneLayerTitle', { defaultMessage: 'Clone layer', diff --git a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_button/toc_entry_button.tsx b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_button/toc_entry_button.tsx index 41c2992c77d88..ffad34454bb61 100644 --- a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_button/toc_entry_button.tsx +++ b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_button/toc_entry_button.tsx @@ -116,7 +116,7 @@ export class TOCEntryButton extends Component { footnotes.push({ icon: , message: i18n.translate('xpack.maps.layer.isUsingSearchMsg', { - defaultMessage: 'Results narrowed by search bar', + defaultMessage: 'Results narrowed by query and filters', }), }); } diff --git a/x-pack/plugins/ml/common/constants/messages.test.ts b/x-pack/plugins/ml/common/constants/messages.test.ts index 1141eea2c176d..59fc50757b674 100644 --- a/x-pack/plugins/ml/common/constants/messages.test.ts +++ b/x-pack/plugins/ml/common/constants/messages.test.ts @@ -35,7 +35,7 @@ describe('Constants: Messages parseMessages()', () => { status: 'success', text: 'Presence of detector functions validated in all detectors.', url: - 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/create-jobs.html#detectors', + 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/ml-ad-finding-anomalies.html#ml-ad-detectors', }, { bucketSpan: '15m', @@ -44,7 +44,7 @@ describe('Constants: Messages parseMessages()', () => { status: 'success', text: 'Format of "15m" is valid and passed validation checks.', url: - 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/create-jobs.html#bucket-span', + 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/ml-ad-finding-anomalies.html#ml-ad-bucket-span', }, { heading: 'Time range', @@ -58,7 +58,7 @@ describe('Constants: Messages parseMessages()', () => { status: 'success', text: 'Valid and within the estimated model memory limit.', url: - 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/create-jobs.html#model-memory-limits', + 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/ml-ad-finding-anomalies.html#ml-ad-model-memory-limits', }, ]); }); @@ -79,7 +79,7 @@ describe('Constants: Messages parseMessages()', () => { status: 'success', text: 'Presence of detector functions validated in all detectors.', url: - 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/create-jobs.html#detectors', + 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/ml-ad-finding-anomalies.html#ml-ad-detectors', }, { bucketSpan: '15m', @@ -116,7 +116,7 @@ describe('Constants: Messages parseMessages()', () => { status: 'success', text: 'Presence of detector functions validated in all detectors.', url: - 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/create-jobs.html#detectors', + 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/ml-ad-finding-anomalies.html#ml-ad-detectors', }, { id: 'cardinality_model_plot_high', @@ -131,7 +131,7 @@ describe('Constants: Messages parseMessages()', () => { text: 'Cardinality of partition_field "order_id" is above 1000 and might result in high memory usage.', url: - 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/create-jobs.html#cardinality', + 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/ml-ad-finding-anomalies.html#ml-ad-cardinality', }, { heading: 'Bucket span', @@ -140,7 +140,7 @@ describe('Constants: Messages parseMessages()', () => { text: 'Bucket span is 1 day or more. Be aware that days are considered as UTC days, not local days.', url: - 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/create-jobs.html#bucket-span', + 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/ml-ad-finding-anomalies.html#ml-ad-bucket-span', }, { bucketSpanCompareFactor: 25, @@ -156,7 +156,7 @@ describe('Constants: Messages parseMessages()', () => { status: 'success', text: 'Influencer configuration passed the validation checks.', url: - 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/ml-influencers.html', + 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/ml-ad-finding-anomalies.html#ml-ad-influencers', }, { id: 'half_estimated_mml_greater_than_mml', @@ -165,7 +165,7 @@ describe('Constants: Messages parseMessages()', () => { text: 'The specified model memory limit is less than half of the estimated model memory limit and will likely hit the hard limit.', url: - 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/create-jobs.html#model-memory-limits', + 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/ml-ad-finding-anomalies.html#ml-ad-model-memory-limits', }, { id: 'missing_summary_count_field_name', diff --git a/x-pack/plugins/ml/common/types/anomalies.ts b/x-pack/plugins/ml/common/types/anomalies.ts index e84035aa50c8f..2bf717067f712 100644 --- a/x-pack/plugins/ml/common/types/anomalies.ts +++ b/x-pack/plugins/ml/common/types/anomalies.ts @@ -12,6 +12,8 @@ export interface Influencer { influencer_field_values: string[]; } +export type MLAnomalyDoc = AnomalyRecordDoc; + export interface AnomalyRecordDoc { [key: string]: any; job_id: string; diff --git a/x-pack/plugins/ml/public/application/components/help_popover/help_popover.tsx b/x-pack/plugins/ml/public/application/components/help_popover/help_popover.tsx index 8cd6a3fbd1138..95c66d58dbb75 100644 --- a/x-pack/plugins/ml/public/application/components/help_popover/help_popover.tsx +++ b/x-pack/plugins/ml/public/application/components/help_popover/help_popover.tsx @@ -6,6 +6,7 @@ */ import React, { ReactNode } from 'react'; +import { i18n } from '@kbn/i18n'; import { EuiButtonIcon, EuiLinkButtonProps, @@ -22,6 +23,9 @@ export const HelpPopoverButton = ({ onClick }: { onClick: EuiLinkButtonProps['on className="mlHelpPopover__buttonIcon" size="s" iconType="help" + aria-label={i18n.translate('xpack.ml.helpPopover.ariaLabel', { + defaultMessage: 'Help', + })} onClick={onClick} /> ); diff --git a/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/revert_model_snapshot_flyout.tsx b/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/revert_model_snapshot_flyout.tsx index 6dd4e6c14589b..f282b2fde2b3a 100644 --- a/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/revert_model_snapshot_flyout.tsx +++ b/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/revert_model_snapshot_flyout.tsx @@ -42,6 +42,7 @@ import { Anomaly } from '../../../jobs/new_job/common/results_loader/results_loa import { parseInterval } from '../../../../../common/util/parse_interval'; import { CreateCalendar, CalendarEvent } from './create_calendar'; import { timeFormatter } from '../../../../../common/util/date_utils'; +import { toastNotificationServiceProvider } from '../../../services/toast_notification_service'; interface Props { snapshot: ModelSnapshot; @@ -139,6 +140,10 @@ export const RevertModelSnapshotFlyout: FC = ({ }) ); refresh(); + }) + .catch((error) => { + const { displayErrorToast } = toastNotificationServiceProvider(toasts); + displayErrorToast(error); }); hideRevertModal(); closeFlyout(); diff --git a/x-pack/plugins/ml/public/application/explorer/actions/load_explorer_data.ts b/x-pack/plugins/ml/public/application/explorer/actions/load_explorer_data.ts index 621ce44204730..45afab0cce4fd 100644 --- a/x-pack/plugins/ml/public/application/explorer/actions/load_explorer_data.ts +++ b/x-pack/plugins/ml/public/application/explorer/actions/load_explorer_data.ts @@ -223,7 +223,9 @@ const loadExplorerDataProvider = ( swimlaneLimit, viewByPerPage, viewByFromPage, - swimlaneContainerWidth + swimlaneContainerWidth, + selectionInfluencers, + influencersFilterQuery ) : Promise.resolve([]), }).pipe( diff --git a/x-pack/plugins/ml/public/application/explorer/components/explorer_query_bar/explorer_query_bar.tsx b/x-pack/plugins/ml/public/application/explorer/components/explorer_query_bar/explorer_query_bar.tsx index 6f5ae5e17590a..57e051e1b8417 100644 --- a/x-pack/plugins/ml/public/application/explorer/components/explorer_query_bar/explorer_query_bar.tsx +++ b/x-pack/plugins/ml/public/application/explorer/components/explorer_query_bar/explorer_query_bar.tsx @@ -95,7 +95,6 @@ function getInitSearchInputState({ interface ExplorerQueryBarProps { filterActive: boolean; - filterIconTriggeredQuery: string; filterPlaceHolder: string; indexPattern: IIndexPattern; queryString?: string; @@ -104,7 +103,6 @@ interface ExplorerQueryBarProps { export const ExplorerQueryBar: FC = ({ filterActive, - filterIconTriggeredQuery, filterPlaceHolder, indexPattern, queryString, @@ -116,14 +114,12 @@ export const ExplorerQueryBar: FC = ({ ); const [errorMessage, setErrorMessage] = useState(undefined); - useEffect(() => { - if (filterIconTriggeredQuery !== undefined) { - setSearchInput({ - language: searchInput.language, - query: filterIconTriggeredQuery, - }); - } - }, [filterIconTriggeredQuery]); + useEffect( + function updateSearchInputFromFilter() { + setSearchInput(getInitSearchInputState({ filterActive, queryString })); + }, + [filterActive, queryString] + ); const searchChangeHandler = (query: Query) => { if (searchInput.language !== query.language) { @@ -131,6 +127,7 @@ export const ExplorerQueryBar: FC = ({ } setSearchInput(query); }; + const applyInfluencersFilterQuery = (query: Query) => { try { const { clearSettings, settings } = getKqlQueryValues({ diff --git a/x-pack/plugins/ml/public/application/explorer/explorer.js b/x-pack/plugins/ml/public/application/explorer/explorer.js index 31058b62af7fe..81474c212d265 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer.js @@ -86,7 +86,6 @@ const ExplorerPage = ({ filterPlaceHolder, indexPattern, queryString, - filterIconTriggeredQuery, updateLanguage, }) => (
@@ -121,7 +120,6 @@ const ExplorerPage = ({ filterPlaceHolder={filterPlaceHolder} indexPattern={indexPattern} queryString={queryString} - filterIconTriggeredQuery={filterIconTriggeredQuery} updateLanguage={updateLanguage} />
@@ -151,7 +149,7 @@ export class ExplorerUI extends React.Component { selectedJobsRunning: PropTypes.bool.isRequired, }; - state = { filterIconTriggeredQuery: undefined, language: DEFAULT_QUERY_LANG }; + state = { language: DEFAULT_QUERY_LANG }; htmlIdGen = htmlIdGenerator(); componentDidMount() { @@ -200,8 +198,6 @@ export class ExplorerUI extends React.Component { } } - this.setState({ filterIconTriggeredQuery: `${newQueryString}` }); - try { const { clearSettings, settings } = getKqlQueryValues({ inputString: `${newQueryString}`, @@ -327,7 +323,6 @@ export class ExplorerUI extends React.Component { influencers={influencers} filterActive={filterActive} filterPlaceHolder={filterPlaceHolder} - filterIconTriggeredQuery={this.state.filterIconTriggeredQuery} indexPattern={indexPattern} queryString={queryString} updateLanguage={this.updateLanguage} diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_utils.d.ts b/x-pack/plugins/ml/public/application/explorer/explorer_utils.d.ts index ebab308b86027..e8abe9e45c09a 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_utils.d.ts +++ b/x-pack/plugins/ml/public/application/explorer/explorer_utils.d.ts @@ -181,15 +181,6 @@ declare interface LoadOverallDataResponse { overallSwimlaneData: OverallSwimlaneData; } -export declare const loadViewByTopFieldValuesForSelectedTime: ( - earliestMs: number, - latestMs: number, - selectedJobs: ExplorerJob[], - viewBySwimlaneFieldName: string, - swimlaneLimit: number, - noInfluencersConfigured: boolean -) => Promise; - export declare interface FilterData { influencersFilterQuery: InfluencersFilterQuery; filterActive: boolean; diff --git a/x-pack/plugins/ml/public/application/explorer/swimlane_container.tsx b/x-pack/plugins/ml/public/application/explorer/swimlane_container.tsx index 82f8a90fafb7d..86ec2014c8339 100644 --- a/x-pack/plugins/ml/public/application/explorer/swimlane_container.tsx +++ b/x-pack/plugins/ml/public/application/explorer/swimlane_container.tsx @@ -411,7 +411,7 @@ export const SwimlaneContainer: FC = ({ > <>
-
+
{showSwimlane && !isLoading && ( = ({ jobsWithTim to: globalState.time.to, }); } - }, [globalState?.time?.from, globalState?.time?.to]); + }, [lastRefresh, globalState?.time?.from, globalState?.time?.to]); const getJobsWithStoppedPartitions = useCallback(async (selectedJobIds: string[]) => { try { 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 ef669a7703c1f..6eb4386276753 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer.tsx @@ -142,10 +142,10 @@ export const TimeSeriesExplorerUrlStateManager: FC d.id); @@ -254,7 +258,9 @@ export class AnomalyTimelineService { latestMs, swimlaneLimit, perPage, - fromPage + fromPage, + selectionInfluencers, + influencersFilterQuery ); if (resp.influencers[viewBySwimlaneFieldName] === undefined) { return []; @@ -276,6 +282,8 @@ export class AnomalyTimelineService { earliestMs, latestMs, this.getSwimlaneBucketInterval(selectedJobs, swimlaneContainerWidth).asMilliseconds(), + perPage, + fromPage, swimlaneLimit ); return Object.keys(resp.results); diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/results.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/results.ts index 25ef36782207f..a9f6dbb45f6e3 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/results.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/results.ts @@ -16,6 +16,11 @@ import { JobId } from '../../../../common/types/anomaly_detection_jobs'; import { JOB_ID, PARTITION_FIELD_VALUE } from '../../../../common/constants/anomalies'; import { PartitionFieldsDefinition } from '../results_service/result_service_rx'; import { PartitionFieldsConfig } from '../../../../common/types/storage'; +import { + ESSearchRequest, + ESSearchResponse, +} from '../../../../../../../src/core/types/elasticsearch'; +import { MLAnomalyDoc } from '../../../../common/types/anomalies'; export const resultsApiProvider = (httpService: HttpService) => ({ getAnomaliesTableData( @@ -112,18 +117,18 @@ export const resultsApiProvider = (httpService: HttpService) => ({ }); }, - anomalySearch(query: any, jobIds: string[]) { + anomalySearch(query: ESSearchRequest, jobIds: string[]) { const body = JSON.stringify({ query, jobIds }); - return httpService.http({ + return httpService.http>({ path: `${basePath()}/results/anomaly_search`, method: 'POST', body, }); }, - anomalySearch$(query: any, jobIds: string[]) { + anomalySearch$(query: ESSearchRequest, jobIds: string[]) { const body = JSON.stringify({ query, jobIds }); - return httpService.http$({ + return httpService.http$>({ path: `${basePath()}/results/anomaly_search`, method: 'POST', body, diff --git a/x-pack/plugins/ml/public/application/services/results_service/results_service.d.ts b/x-pack/plugins/ml/public/application/services/results_service/results_service.d.ts index ea07d32bfff1d..1848b13cb5a1f 100644 --- a/x-pack/plugins/ml/public/application/services/results_service/results_service.d.ts +++ b/x-pack/plugins/ml/public/application/services/results_service/results_service.d.ts @@ -23,7 +23,8 @@ export function resultsServiceProvider( intervalMs: number, perPage?: number, fromPage?: number, - swimLaneSeverity?: number + swimLaneSeverity?: number, + influencersFilterQuery?: InfluencersFilterQuery ): Promise; getTopInfluencers( selectedJobIds: string[], @@ -32,7 +33,7 @@ export function resultsServiceProvider( maxFieldValues: number, perPage?: number, fromPage?: number, - influencers?: any[], + influencers?: EntityField[], influencersFilterQuery?: InfluencersFilterQuery ): Promise; getTopInfluencerValues(): Promise; diff --git a/x-pack/plugins/ml/server/models/job_service/model_snapshots.ts b/x-pack/plugins/ml/server/models/job_service/model_snapshots.ts index 6cb5f67149fb6..56221f9a72c89 100644 --- a/x-pack/plugins/ml/server/models/job_service/model_snapshots.ts +++ b/x-pack/plugins/ml/server/models/job_service/model_snapshots.ts @@ -85,7 +85,6 @@ export function modelSnapshotProvider(client: IScopedClusterClient, mlClient: Ml ), events: calendarEvents.map((s) => ({ calendar_id: calendarId, - event_id: '', description: s.description, start_time: `${s.start}`, end_time: `${s.end}`, diff --git a/x-pack/plugins/observability/public/components/app/fleet_panel/index.tsx b/x-pack/plugins/observability/public/components/app/fleet_panel/index.tsx index fce1cde38f587..b6db8b2308b78 100644 --- a/x-pack/plugins/observability/public/components/app/fleet_panel/index.tsx +++ b/x-pack/plugins/observability/public/components/app/fleet_panel/index.tsx @@ -16,9 +16,6 @@ export function FleetPanel() { return ( {i18n.translate('xpack.observability.fleet.text', { @@ -30,7 +27,7 @@ export function FleetPanel() { footer={ {i18n.translate('xpack.observability.fleet.button', { - defaultMessage: 'Try Fleet Beta', + defaultMessage: 'Try Fleet', })} } diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_storage.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_storage.test.tsx new file mode 100644 index 0000000000000..c32acc47abd1b --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_storage.test.tsx @@ -0,0 +1,131 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useEffect } from 'react'; + +import { UrlStorageContextProvider, useSeriesStorage } from './use_series_storage'; +import { render } from '@testing-library/react'; + +const mockSingleSeries = { + 'performance-distribution': { + reportType: 'data-distribution', + dataType: 'ux', + breakdown: 'user_agent.name', + time: { from: 'now-15m', to: 'now' }, + }, +}; + +const mockMultipleSeries = { + 'performance-distribution': { + reportType: 'data-distribution', + dataType: 'ux', + breakdown: 'user_agent.name', + time: { from: 'now-15m', to: 'now' }, + }, + 'kpi-over-time': { + reportType: 'kpi-over-time', + dataType: 'synthetics', + breakdown: 'user_agent.name', + time: { from: 'now-15m', to: 'now' }, + }, +}; + +describe('userSeries', function () { + function setupTestComponent(seriesData: any) { + const setData = jest.fn(); + function TestComponent() { + const data = useSeriesStorage(); + + useEffect(() => { + setData(data); + }, [data]); + + return Test; + } + + render( + + + + ); + + return setData; + } + it('should return expected result when there is one series', function () { + const setData = setupTestComponent(mockSingleSeries); + + expect(setData).toHaveBeenCalledTimes(2); + expect(setData).toHaveBeenLastCalledWith( + expect.objectContaining({ + allSeries: { + 'performance-distribution': { + breakdown: 'user_agent.name', + dataType: 'ux', + reportType: 'data-distribution', + time: { from: 'now-15m', to: 'now' }, + }, + }, + allSeriesIds: ['performance-distribution'], + firstSeries: { + breakdown: 'user_agent.name', + dataType: 'ux', + reportType: 'data-distribution', + time: { from: 'now-15m', to: 'now' }, + }, + firstSeriesId: 'performance-distribution', + }) + ); + }); + + it('should return expected result when there are multiple series series', function () { + const setData = setupTestComponent(mockMultipleSeries); + + expect(setData).toHaveBeenCalledTimes(2); + expect(setData).toHaveBeenLastCalledWith( + expect.objectContaining({ + allSeries: { + 'performance-distribution': { + breakdown: 'user_agent.name', + dataType: 'ux', + reportType: 'data-distribution', + time: { from: 'now-15m', to: 'now' }, + }, + 'kpi-over-time': { + reportType: 'kpi-over-time', + dataType: 'synthetics', + breakdown: 'user_agent.name', + time: { from: 'now-15m', to: 'now' }, + }, + }, + allSeriesIds: ['performance-distribution', 'kpi-over-time'], + firstSeries: { + breakdown: 'user_agent.name', + dataType: 'ux', + reportType: 'data-distribution', + time: { from: 'now-15m', to: 'now' }, + }, + firstSeriesId: 'performance-distribution', + }) + ); + }); + + it('should return expected result when there are no series', function () { + const setData = setupTestComponent({}); + + expect(setData).toHaveBeenCalledTimes(2); + expect(setData).toHaveBeenLastCalledWith( + expect.objectContaining({ + allSeries: {}, + allSeriesIds: [], + firstSeries: undefined, + firstSeriesId: undefined, + }) + ); + }); +}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_storage.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_storage.tsx index 0add5a19a95cc..a47a124d14b4d 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_storage.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_storage.tsx @@ -67,7 +67,7 @@ export function UrlStorageContextProvider({ setAllSeries(allSeriesN); setFirstSeriesId(allSeriesIds?.[0]); - setFirstSeries(allSeriesN?.[0]); + setFirstSeries(allSeriesN?.[allSeriesIds?.[0]]); (storage as IKbnUrlStateStorage).set(allSeriesKey, allShortSeries); }, [allShortSeries, storage]); diff --git a/x-pack/plugins/observability/public/index.ts b/x-pack/plugins/observability/public/index.ts index 0561eab08fb45..6bafe465fd024 100644 --- a/x-pack/plugins/observability/public/index.ts +++ b/x-pack/plugins/observability/public/index.ts @@ -68,5 +68,9 @@ export { createExploratoryViewUrl } from './components/shared/exploratory_view/c export { FilterValueLabel } from './components/shared/filter_value_label/filter_value_label'; export type { SeriesUrl } from './components/shared/exploratory_view/types'; -export type { ObservabilityRuleTypeRegistry } from './rules/create_observability_rule_type_registry'; +export type { + ObservabilityRuleTypeFormatter, + ObservabilityRuleTypeModel, + ObservabilityRuleTypeRegistry, +} from './rules/create_observability_rule_type_registry'; export { createObservabilityRuleTypeRegistryMock } from './rules/observability_rule_type_registry_mock'; diff --git a/x-pack/plugins/observability/public/rules/create_observability_rule_type_registry.ts b/x-pack/plugins/observability/public/rules/create_observability_rule_type_registry.ts index 35f2dc18c2f22..d6f8c08359888 100644 --- a/x-pack/plugins/observability/public/rules/create_observability_rule_type_registry.ts +++ b/x-pack/plugins/observability/public/rules/create_observability_rule_type_registry.ts @@ -5,19 +5,29 @@ * 2.0. */ -import { AlertTypeModel, AlertTypeRegistryContract } from '../../../triggers_actions_ui/public'; +import { + AlertTypeModel, + AlertTypeParams, + AlertTypeRegistryContract, +} from '../../../triggers_actions_ui/public'; import { ParsedTechnicalFields } from '../../../rule_registry/common/parse_technical_fields'; import { AsDuration, AsPercent } from '../../common/utils/formatters'; -export type Formatter = (options: { +export type ObservabilityRuleTypeFormatter = (options: { fields: ParsedTechnicalFields & Record; formatters: { asDuration: AsDuration; asPercent: AsPercent }; }) => { reason: string; link: string }; +export interface ObservabilityRuleTypeModel + extends AlertTypeModel { + format: ObservabilityRuleTypeFormatter; +} + export function createObservabilityRuleTypeRegistry(alertTypeRegistry: AlertTypeRegistryContract) { - const formatters: Array<{ typeId: string; fn: Formatter }> = []; + const formatters: Array<{ typeId: string; fn: ObservabilityRuleTypeFormatter }> = []; + return { - register: (type: AlertTypeModel & { format: Formatter }) => { + register: (type: ObservabilityRuleTypeModel) => { const { format, ...rest } = type; formatters.push({ typeId: type.id, fn: format }); alertTypeRegistry.register(rest); diff --git a/x-pack/plugins/observability/server/plugin.ts b/x-pack/plugins/observability/server/plugin.ts index 3e8f511eb1153..868e234fcb2a1 100644 --- a/x-pack/plugins/observability/server/plugin.ts +++ b/x-pack/plugins/observability/server/plugin.ts @@ -38,47 +38,49 @@ export class ObservabilityPlugin implements Plugin { } public setup(core: CoreSetup, plugins: PluginSetup) { - plugins.features.registerKibanaFeature({ - id: casesFeatureId, - name: i18n.translate('xpack.observability.featureRegistry.linkObservabilityTitle', { - defaultMessage: 'Cases', - }), - order: 1100, - category: DEFAULT_APP_CATEGORIES.observability, - app: [casesFeatureId, 'kibana'], - catalogue: [observabilityFeatureId], - cases: [observabilityFeatureId], - privileges: { - all: { - app: [casesFeatureId, 'kibana'], - catalogue: [observabilityFeatureId], - cases: { - all: [observabilityFeatureId], - }, - api: [], - savedObject: { - all: [], - read: [], - }, - ui: ['crud_cases', 'read_cases'], // uiCapabilities[casesFeatureId].crud_cases or read_cases - }, - read: { - app: [casesFeatureId, 'kibana'], - catalogue: [observabilityFeatureId], - cases: { - read: [observabilityFeatureId], + const config = this.initContext.config.get(); + + if (config.unsafe.cases.enabled) { + plugins.features.registerKibanaFeature({ + id: casesFeatureId, + name: i18n.translate('xpack.observability.featureRegistry.linkObservabilityTitle', { + defaultMessage: 'Cases', + }), + order: 1100, + category: DEFAULT_APP_CATEGORIES.observability, + app: [casesFeatureId, 'kibana'], + catalogue: [observabilityFeatureId], + cases: [observabilityFeatureId], + privileges: { + all: { + app: [casesFeatureId, 'kibana'], + catalogue: [observabilityFeatureId], + cases: { + all: [observabilityFeatureId], + }, + api: [], + savedObject: { + all: [], + read: [], + }, + ui: ['crud_cases', 'read_cases'], // uiCapabilities[casesFeatureId].crud_cases or read_cases }, - api: [], - savedObject: { - all: [], - read: [], + read: { + app: [casesFeatureId, 'kibana'], + catalogue: [observabilityFeatureId], + cases: { + read: [observabilityFeatureId], + }, + api: [], + savedObject: { + all: [], + read: [], + }, + ui: ['read_cases'], // uiCapabilities[uiCapabilities[casesFeatureId]].read_cases }, - ui: ['read_cases'], // uiCapabilities[uiCapabilities[casesFeatureId]].read_cases }, - }, - }); - - const config = this.initContext.config.get(); + }); + } let annotationsApiPromise: Promise | undefined; diff --git a/x-pack/plugins/osquery/public/saved_queries/saved_queries_dropdown.tsx b/x-pack/plugins/osquery/public/saved_queries/saved_queries_dropdown.tsx index 30df2267fbfa1..fc7cee2fc804c 100644 --- a/x-pack/plugins/osquery/public/saved_queries/saved_queries_dropdown.tsx +++ b/x-pack/plugins/osquery/public/saved_queries/saved_queries_dropdown.tsx @@ -6,7 +6,7 @@ */ import { find } from 'lodash/fp'; -import { EuiCodeBlock, EuiFormRow, EuiComboBox, EuiText } from '@elastic/eui'; +import { EuiCodeBlock, EuiFormRow, EuiComboBox, EuiTextColor } from '@elastic/eui'; import React, { forwardRef, useCallback, @@ -19,6 +19,7 @@ import { SimpleSavedObject } from 'kibana/public'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { useHistory, useLocation } from 'react-router-dom'; +import styled from 'styled-components'; import { useSavedQueries } from './use_saved_queries'; @@ -26,6 +27,17 @@ export interface SavedQueriesDropdownRef { clearSelection: () => void; } +const TextTruncate = styled.div` + overflow: hidden; + text-overflow: ellipsis; +`; + +const StyledEuiCodeBlock = styled(EuiCodeBlock)` + .euiCodeBlock__line { + white-space: nowrap; + } +`; + interface SavedQueriesDropdownProps { disabled?: boolean; onChange: ( @@ -88,12 +100,12 @@ const SavedQueriesDropdownComponent = forwardRef< ({ value }) => ( <> {value.id} - -

{value.description}

-
- - {value.query} - + + {value.description} + + + {value.query.split('\n').join(' ')} + ), [] @@ -145,7 +157,7 @@ const SavedQueriesDropdownComponent = forwardRef< selectedOptions={selectedOptions} onChange={handleSavedQueryChange} renderOption={renderOption} - rowHeight={90} + rowHeight={110} /> ); diff --git a/x-pack/plugins/osquery/public/saved_queries/use_update_saved_query.ts b/x-pack/plugins/osquery/public/saved_queries/use_update_saved_query.ts index 1260413676a4e..6f4aa51710811 100644 --- a/x-pack/plugins/osquery/public/saved_queries/use_update_saved_query.ts +++ b/x-pack/plugins/osquery/public/saved_queries/use_update_saved_query.ts @@ -56,7 +56,7 @@ export const useUpdateSavedQuery = ({ savedQueryId }: UseUpdateSavedQueryProps) i18n.translate('xpack.osquery.editSavedQuery.successToastMessageText', { defaultMessage: 'Successfully updated "{savedQueryName}" query', values: { - savedQueryName: payload.attributes?.name ?? '', + savedQueryName: payload.attributes?.id ?? '', }, }) ); diff --git a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js index 6969f98e5f092..5c8ef874ec7ed 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js @@ -494,6 +494,7 @@ export class DetailPanel extends Component { aria-labelledby="remoteClusterDetailsFlyoutTitle" size="m" maxWidth={550} + className="eui-textBreakAll" > diff --git a/x-pack/plugins/reporting/common/types.ts b/x-pack/plugins/reporting/common/types.ts index 193f3f2a971ea..308245a696d92 100644 --- a/x-pack/plugins/reporting/common/types.ts +++ b/x-pack/plugins/reporting/common/types.ts @@ -55,31 +55,39 @@ export interface TaskRunResult { } export interface ReportSource { - jobtype: string; - kibana_name: string; - kibana_id: string; - created_by: string | false; + /* + * Required fields: populated in enqueue_job when the request comes in to + * generate the report + */ + jobtype: string; // refers to `ExportTypeDefinition.jobType` + created_by: string | false; // username or `false` if security is disabled. Used for ensuring users can only access the reports they've created. payload: { headers: string; // encrypted headers - browserTimezone?: string; // may use timezone from advanced settings - objectType: string; - title: string; - layout?: LayoutParams; - isDeprecated?: boolean; - }; - meta: { objectType: string; layout?: string }; - browser_type: string; - migration_version: string; - max_attempts: number; - timeout: number; - + isDeprecated?: boolean; // set to true when the export type is being phased out + } & BaseParams; + meta: { objectType: string; layout?: string }; // for telemetry + migration_version: string; // for reminding the user to update their POST URL + attempts: number; // initially populated as 0 + created_at: string; // timestamp in UTC status: JobStatus; - attempts: number; + + /* + * `output` is only populated if the report job is completed or failed. + */ output: TaskRunResult | null; - started_at?: string; - completed_at?: string; - created_at: string; - process_expiration?: string | null; // must be set to null to clear the expiration + + /* + * Optional fields: populated when the job is claimed to execute, and after + * execution has finished + */ + kibana_name?: string; // for troubleshooting + kibana_id?: string; // for troubleshooting + browser_type?: string; // no longer used since chromium is the only option (used to allow phantomjs) + timeout?: number; // for troubleshooting: the actual comparison uses the config setting xpack.reporting.queue.timeout + max_attempts?: number; // for troubleshooting: the actual comparison uses the config setting xpack.reporting.capture.maxAttempts + started_at?: string; // timestamp in UTC + completed_at?: string; // timestamp in UTC + process_expiration?: string | null; // timestamp in UTC - is overwritten with `null` when the job needs a retry } /* @@ -97,48 +105,41 @@ export interface BaseParams { } export type JobId = string; + +/* + * JobStatus: + * - Begins as 'pending' + * - Changes to 'processing` when the job is claimed + * - Then 'completed' | 'failed' when execution is done + * If the job needs a retry, it reverts back to 'pending'. + */ export type JobStatus = - | 'completed' - | 'completed_with_warnings' - | 'pending' - | 'processing' - | 'failed'; + | 'completed' // Report was successful + | 'completed_with_warnings' // The download available for troubleshooting - it **should** show a meaningful error + | 'pending' // Report job is waiting to be claimed + | 'processing' // Report job has been claimed and is executing + | 'failed'; // Report was not successful, and all retries are done. Nothing to download. export interface JobContent { content: string; } -export interface ReportApiJSON { +/* + * Info API response: to avoid unnecessary large payloads on a network, the + * report query results do not include `payload.headers` or `output.content`, + * which can be long strings of meaningless text + */ +interface ReportSimple extends Omit { + payload: Omit; + output?: Omit; // is undefined for report jobs that are not completed +} + +/* + * The response format for all of the report job APIs + */ +export interface ReportApiJSON extends ReportSimple { id: string; index: string; - kibana_name: string; - kibana_id: string; - browser_type: string | undefined; - created_at: string; - jobtype: string; - created_by: string | false; - timeout?: number; - output?: { - content_type: string; - size: number; - warnings?: string[]; - }; - process_expiration?: string; - completed_at: string | undefined; - payload: { - layout?: LayoutParams; - title: string; - browserTimezone?: string; - isDeprecated?: boolean; - }; - meta: { - layout?: string; - objectType: string; - }; - max_attempts: number; - started_at: string | undefined; - attempts: number; - status: string; } export interface LicenseCheckResults { @@ -147,13 +148,14 @@ export interface LicenseCheckResults { message: string; } +/* Notifier Toasts */ export interface JobSummary { id: JobId; status: JobStatus; - title: string; - jobtype: string; - maxSizeReached?: boolean; - csvContainsFormulas?: boolean; + jobtype: ReportSource['jobtype']; + title: ReportSource['payload']['title']; + maxSizeReached: TaskRunResult['max_size_reached']; + csvContainsFormulas: TaskRunResult['csv_contains_formulas']; } export interface JobSummarySet { diff --git a/x-pack/plugins/reporting/public/lib/job.ts b/x-pack/plugins/reporting/public/lib/job.ts new file mode 100644 index 0000000000000..c882e8b92986b --- /dev/null +++ b/x-pack/plugins/reporting/public/lib/job.ts @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { JobId, ReportApiJSON, ReportSource, TaskRunResult } from '../../common/types'; + +type ReportPayload = ReportSource['payload']; + +/* + * This class represents a report job for the UI + * It can be instantiated with ReportApiJSON: the response data format for the report job APIs + */ +export class Job { + public id: JobId; + public index: string; + + public objectType: ReportPayload['objectType']; + public title: ReportPayload['title']; + public isDeprecated: ReportPayload['isDeprecated']; + public browserTimezone?: ReportPayload['browserTimezone']; + public layout: ReportPayload['layout']; + + public jobtype: ReportSource['jobtype']; + public created_by: ReportSource['created_by']; + public created_at: ReportSource['created_at']; + public started_at: ReportSource['started_at']; + public completed_at: ReportSource['completed_at']; + public status: ReportSource['status']; + public attempts: ReportSource['attempts']; + public max_attempts: ReportSource['max_attempts']; + + public timeout: ReportSource['timeout']; + public kibana_name: ReportSource['kibana_name']; + public kibana_id: ReportSource['kibana_id']; + public browser_type: ReportSource['browser_type']; + + public size?: TaskRunResult['size']; + public content_type?: TaskRunResult['content_type']; + public csv_contains_formulas?: TaskRunResult['csv_contains_formulas']; + public max_size_reached?: TaskRunResult['max_size_reached']; + public warnings?: TaskRunResult['warnings']; + + constructor(report: ReportApiJSON) { + this.id = report.id; + this.index = report.index; + + this.jobtype = report.jobtype; + this.objectType = report.payload.objectType; + this.title = report.payload.title; + this.layout = report.payload.layout; + this.created_by = report.created_by; + this.created_at = report.created_at; + this.started_at = report.started_at; + this.completed_at = report.completed_at; + this.status = report.status; + this.attempts = report.attempts; + this.max_attempts = report.max_attempts; + + this.timeout = report.timeout; + this.kibana_name = report.kibana_name; + this.kibana_id = report.kibana_id; + this.browser_type = report.browser_type; + this.browserTimezone = report.payload.browserTimezone; + this.size = report.output?.size; + this.content_type = report.output?.content_type; + + this.isDeprecated = report.payload.isDeprecated || false; + this.csv_contains_formulas = report.output?.csv_contains_formulas; + this.max_size_reached = report.output?.max_size_reached; + this.warnings = report.output?.warnings; + } +} diff --git a/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts b/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts index 64caac0e27bdd..90411884332c8 100644 --- a/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts +++ b/x-pack/plugins/reporting/public/lib/reporting_api_client/reporting_api_client.ts @@ -15,37 +15,52 @@ import { API_MIGRATE_ILM_POLICY_URL, REPORTING_MANAGEMENT_HOME, } from '../../../common/constants'; -import { - DownloadReportFn, - JobId, - ManagementLinkFn, - ReportApiJSON, - ReportDocument, - ReportSource, -} from '../../../common/types'; +import { DownloadReportFn, JobId, ManagementLinkFn, ReportApiJSON } from '../../../common/types'; import { add } from '../../notifier/job_completion_notifications'; - -export interface JobQueueEntry { - _id: string; - _source: ReportSource; -} +import { Job } from '../job'; export interface JobContent { content: string; content_type: boolean; } -interface JobParams { - [paramName: string]: any; -} - export interface DiagnoseResponse { help: string[]; success: boolean; logs: string; } -export class ReportingAPIClient { +interface JobParams { + [paramName: string]: any; +} + +interface IReportingAPI { + // Helpers + getReportURL(jobId: string): string; + getReportingJobPath(exportType: string, jobParams: JobParams): string; // Return a URL to queue a job, with the job params encoded in the query string of the URL. Used for copying POST URL + createReportingJob(exportType: string, jobParams: any): Promise; // Sends a request to queue a job, with the job params in the POST body + getServerBasePath(): string; // Provides the raw server basePath to allow it to be stripped out from relativeUrls in job params + + // CRUD + downloadReport(jobId: string): void; + deleteReport(jobId: string): Promise; + list(page: number, jobIds: string[]): Promise; // gets the first 10 report of the page + total(): Promise; + getError(jobId: string): Promise; + getInfo(jobId: string): Promise; + findForJobIds(jobIds: string[]): Promise; + + // Function props + getManagementLink: ManagementLinkFn; + getDownloadLink: DownloadReportFn; + + // Diagnostic-related API calls + verifyConfig(): Promise; + verifyBrowser(): Promise; + verifyScreenCapture(): Promise; +} + +export class ReportingAPIClient implements IReportingAPI { private http: HttpSetup; constructor(http: HttpSetup) { @@ -71,68 +86,69 @@ export class ReportingAPIClient { }); } - public list = (page = 0, jobIds: string[] = []): Promise => { + public async list(page = 0, jobIds: string[] = []) { const query = { page } as any; if (jobIds.length > 0) { // Only getting the first 10, to prevent URL overflows query.ids = jobIds.slice(0, 10).join(','); } - return this.http.get(`${API_LIST_URL}/list`, { + const jobQueueEntries: ReportApiJSON[] = await this.http.get(`${API_LIST_URL}/list`, { query, asSystemRequest: true, }); - }; - public total(): Promise { - return this.http.get(`${API_LIST_URL}/count`, { + return jobQueueEntries.map((report) => new Job(report)); + } + + public async total() { + return await this.http.get(`${API_LIST_URL}/count`, { asSystemRequest: true, }); } - public getContent(jobId: string): Promise { - return this.http.get(`${API_LIST_URL}/output/${jobId}`, { + public async getError(jobId: string) { + return await this.http.get(`${API_LIST_URL}/output/${jobId}`, { asSystemRequest: true, }); } - public getInfo(jobId: string): Promise { - return this.http.get(`${API_LIST_URL}/info/${jobId}`, { + public async getInfo(jobId: string) { + const report: ReportApiJSON = await this.http.get(`${API_LIST_URL}/info/${jobId}`, { asSystemRequest: true, }); + return new Job(report); } - public findForJobIds = (jobIds: JobId[]): Promise => { - return this.http.fetch(`${API_LIST_URL}/list`, { + public async findForJobIds(jobIds: JobId[]) { + const reports: ReportApiJSON[] = await this.http.fetch(`${API_LIST_URL}/list`, { query: { page: 0, ids: jobIds.join(',') }, method: 'GET', }); - }; + return reports.map((report) => new Job(report)); + } - /* - * Return a URL to queue a job, with the job params encoded in the query string of the URL. Used for copying POST URL - */ - public getReportingJobPath = (exportType: string, jobParams: JobParams) => { + public getReportingJobPath(exportType: string, jobParams: JobParams) { const params = stringify({ jobParams: rison.encode(jobParams) }); return `${this.http.basePath.prepend(API_BASE_GENERATE)}/${exportType}?${params}`; - }; + } - /* - * Sends a request to queue a job, with the job params in the POST body - */ - public createReportingJob = async (exportType: string, jobParams: any) => { + public async createReportingJob(exportType: string, jobParams: any) { const jobParamsRison = rison.encode(jobParams); - const resp = await this.http.post(`${API_BASE_GENERATE}/${exportType}`, { - method: 'POST', - body: JSON.stringify({ - jobParams: jobParamsRison, - }), - }); + const resp: { job: ReportApiJSON } = await this.http.post( + `${API_BASE_GENERATE}/${exportType}`, + { + method: 'POST', + body: JSON.stringify({ + jobParams: jobParamsRison, + }), + } + ); add(resp.job.id); - return resp; - }; + return new Job(resp.job); + } public getManagementLink: ManagementLinkFn = () => this.http.basePath.prepend(REPORTING_MANAGEMENT_HOME); @@ -140,36 +156,27 @@ export class ReportingAPIClient { public getDownloadLink: DownloadReportFn = (jobId: JobId) => this.http.basePath.prepend(`${API_LIST_URL}/download/${jobId}`); - /* - * provides the raw server basePath to allow it to be stripped out from relativeUrls in job params - */ public getServerBasePath = () => this.http.basePath.serverBasePath; - /* - * Diagnostic-related API calls - */ - public verifyConfig = (): Promise => - this.http.post(`${API_BASE_URL}/diagnose/config`, { + public async verifyConfig() { + return await this.http.post(`${API_BASE_URL}/diagnose/config`, { asSystemRequest: true, }); + } - /* - * Diagnostic-related API calls - */ - public verifyBrowser = (): Promise => - this.http.post(`${API_BASE_URL}/diagnose/browser`, { + public async verifyBrowser() { + return await this.http.post(`${API_BASE_URL}/diagnose/browser`, { asSystemRequest: true, }); + } - /* - * Diagnostic-related API calls - */ - public verifyScreenCapture = (): Promise => - this.http.post(`${API_BASE_URL}/diagnose/screenshot`, { + public async verifyScreenCapture() { + return await this.http.post(`${API_BASE_URL}/diagnose/screenshot`, { asSystemRequest: true, }); + } - public migrateReportingIndicesIlmPolicy = (): Promise => { - return this.http.put(`${API_MIGRATE_ILM_POLICY_URL}`); - }; + public async migrateReportingIndicesIlmPolicy() { + return await this.http.put(`${API_MIGRATE_ILM_POLICY_URL}`); + } } diff --git a/x-pack/plugins/reporting/public/lib/stream_handler.test.ts b/x-pack/plugins/reporting/public/lib/stream_handler.test.ts index 7c3837486ad1d..58fde5cbd83ad 100644 --- a/x-pack/plugins/reporting/public/lib/stream_handler.test.ts +++ b/x-pack/plugins/reporting/public/lib/stream_handler.test.ts @@ -7,7 +7,9 @@ import sinon, { stub } from 'sinon'; import { NotificationsStart } from 'src/core/public'; -import { JobSummary, ReportDocument } from '../../common/types'; +import { coreMock } from '../../../../../src/core/public/mocks'; +import { JobSummary, ReportApiJSON } from '../../common/types'; +import { Job } from './job'; import { ReportingAPIClient } from './reporting_api_client'; import { ReportingNotifierStreamHandler } from './stream_handler'; @@ -18,43 +20,20 @@ Object.defineProperty(window, 'sessionStorage', { writable: true, }); -const mockJobsFound = [ - { - _id: 'job-source-mock1', - _source: { - status: 'completed', - output: { max_size_reached: false, csv_contains_formulas: false }, - payload: { title: 'specimen' }, - }, - }, - { - _id: 'job-source-mock2', - _source: { - status: 'failed', - output: { max_size_reached: false, csv_contains_formulas: false }, - payload: { title: 'specimen' }, - }, - }, - { - _id: 'job-source-mock3', - _source: { - status: 'pending', - output: { max_size_reached: false, csv_contains_formulas: false }, - payload: { title: 'specimen' }, - }, - }, -]; +const mockJobsFound: Job[] = [ + { id: 'job-source-mock1', status: 'completed', output: { csv_contains_formulas: false, max_size_reached: false }, payload: { title: 'specimen' } }, + { id: 'job-source-mock2', status: 'failed', output: { csv_contains_formulas: false, max_size_reached: false }, payload: { title: 'specimen' } }, + { id: 'job-source-mock3', status: 'pending', output: { csv_contains_formulas: false, max_size_reached: false }, payload: { title: 'specimen' } }, +].map((j) => new Job(j as ReportApiJSON)); // prettier-ignore -const jobQueueClientMock: ReportingAPIClient = { - findForJobIds: async (jobIds: string[]) => { - return mockJobsFound as ReportDocument[]; - }, - getContent: (): Promise => { - return Promise.resolve({ content: 'this is the completed report data' }); - }, - getManagementLink: () => '/#management', - getDownloadLink: () => '/reporting/download/job-123', -} as any; +const jobQueueClientMock = new ReportingAPIClient(coreMock.createSetup().http); +jobQueueClientMock.findForJobIds = async (jobIds: string[]) => mockJobsFound; +jobQueueClientMock.getInfo = () => + Promise.resolve(({ content: 'this is the completed report data' } as unknown) as Job); +jobQueueClientMock.getError = () => + Promise.resolve({ content: 'this is the completed report data' }); +jobQueueClientMock.getManagementLink = () => '/#management'; +jobQueueClientMock.getDownloadLink = () => '/reporting/download/job-123'; const mockShowDanger = stub(); const mockShowSuccess = stub(); diff --git a/x-pack/plugins/reporting/public/lib/stream_handler.ts b/x-pack/plugins/reporting/public/lib/stream_handler.ts index 53191cacb5ba1..8e41d34d054ec 100644 --- a/x-pack/plugins/reporting/public/lib/stream_handler.ts +++ b/x-pack/plugins/reporting/public/lib/stream_handler.ts @@ -10,7 +10,7 @@ import * as Rx from 'rxjs'; import { catchError, map } from 'rxjs/operators'; import { NotificationsSetup } from 'src/core/public'; import { JOB_COMPLETION_NOTIFICATIONS_SESSION_KEY, JOB_STATUSES } from '../../common/constants'; -import { JobId, JobSummary, JobSummarySet, ReportDocument } from '../../common/types'; +import { JobId, JobSummary, JobSummarySet } from '../../common/types'; import { getFailureToast, getGeneralErrorToast, @@ -18,20 +18,21 @@ import { getWarningFormulasToast, getWarningMaxSizeToast, } from '../notifier'; +import { Job } from './job'; import { ReportingAPIClient } from './reporting_api_client'; function updateStored(jobIds: JobId[]): void { sessionStorage.setItem(JOB_COMPLETION_NOTIFICATIONS_SESSION_KEY, JSON.stringify(jobIds)); } -function getReportStatus(src: ReportDocument): JobSummary { +function getReportStatus(src: Job): JobSummary { return { - id: src._id, - status: src._source.status, - title: src._source.payload.title, - jobtype: src._source.jobtype, - maxSizeReached: src._source.output?.max_size_reached, - csvContainsFormulas: src._source.output?.csv_contains_formulas, + id: src.id, + status: src.status, + title: src.title, + jobtype: src.jobtype, + maxSizeReached: src.max_size_reached, + csvContainsFormulas: src.csv_contains_formulas, }; } @@ -73,7 +74,7 @@ export class ReportingNotifierStreamHandler { // no download link available for (const job of failedJobs) { - const { content } = await this.apiClient.getContent(job.id); + const { content } = await this.apiClient.getError(job.id); this.notifications.toasts.addDanger( getFailureToast(content, job, this.apiClient.getManagementLink) ); @@ -90,17 +91,14 @@ export class ReportingNotifierStreamHandler { */ public findChangedStatusJobs(storedJobs: JobId[]): Rx.Observable { return Rx.from(this.apiClient.findForJobIds(storedJobs)).pipe( - map((jobs: ReportDocument[]) => { + map((jobs) => { const completedJobs: JobSummary[] = []; const failedJobs: JobSummary[] = []; const pending: JobId[] = []; // add side effects to storage for (const job of jobs) { - const { - _id: jobId, - _source: { status: jobStatus }, - } = job; + const { id: jobId, status: jobStatus } = job; if (storedJobs.includes(jobId)) { if (jobStatus === JOB_STATUSES.COMPLETED || jobStatus === JOB_STATUSES.WARNINGS) { completedJobs.push(getReportStatus(job)); diff --git a/x-pack/plugins/reporting/public/management/__snapshots__/report_info_button.test.tsx.snap b/x-pack/plugins/reporting/public/management/__snapshots__/report_info_button.test.tsx.snap index cb36584960867..3417aa59f9d72 100644 --- a/x-pack/plugins/reporting/public/management/__snapshots__/report_info_button.test.tsx.snap +++ b/x-pack/plugins/reporting/public/management/__snapshots__/report_info_button.test.tsx.snap @@ -6,7 +6,9 @@ exports[`ReportInfoButton handles button click flyout on click 1`] = ` className="euiButtonIcon euiButtonIcon--primary euiButtonIcon--empty euiButtonIcon--xSmall" data-test-subj="reportInfoButton" disabled={false} + onBlur={[Function]} onClick={[Function]} + onFocus={[Function]} type="button" >

- - - - - + + + + + + +
- - - - - + + + + + + + @@ -1404,7 +1654,7 @@ exports[`ReportListing Report job listing with some items 1`] = ` - - - - - - - - - - - - -
-
- - -
- + + + + + + + + + +
+
+ + + +
+
+ + +
+ - + - - - - + + + + + + +
@@ -3493,7 +4243,7 @@ exports[`ReportListing Report job listing with some items 1`] = ` - + - - - - + + + + + + + @@ -4594,7 +5594,7 @@ exports[`ReportListing Report job listing with some items 1`] = ` - + - - - - + + + + + + + @@ -5662,7 +6912,7 @@ exports[`ReportListing Report job listing with some items 1`] = ` - + - - - - + + + + + + + @@ -6730,7 +8230,7 @@ exports[`ReportListing Report job listing with some items 1`] = ` - + - - - - + + + + + + + @@ -7798,7 +9548,7 @@ exports[`ReportListing Report job listing with some items 1`] = ` - + - - - - + + + + + + + @@ -8866,7 +10866,7 @@ exports[`ReportListing Report job listing with some items 1`] = ` - - - - - + + + + + + + diff --git a/x-pack/plugins/reporting/public/management/ilm_policy_link.tsx b/x-pack/plugins/reporting/public/management/ilm_policy_link.tsx index 3945ec5be9fa7..a40f167de5bc3 100644 --- a/x-pack/plugins/reporting/public/management/ilm_policy_link.tsx +++ b/x-pack/plugins/reporting/public/management/ilm_policy_link.tsx @@ -21,7 +21,7 @@ interface Props { const i18nTexts = { buttonLabel: i18n.translate('xpack.reporting.listing.reports.ilmPolicyLinkText', { - defaultMessage: 'Edit ILM policy', + defaultMessage: 'Edit reporting ILM policy', }), }; diff --git a/x-pack/plugins/reporting/public/management/migrate_ilm_policy_callout/ilm_policy_migration_needed_callout.tsx b/x-pack/plugins/reporting/public/management/migrate_ilm_policy_callout/ilm_policy_migration_needed_callout.tsx index 5bb3ac524e130..e96cb842d55cf 100644 --- a/x-pack/plugins/reporting/public/management/migrate_ilm_policy_callout/ilm_policy_migration_needed_callout.tsx +++ b/x-pack/plugins/reporting/public/management/migrate_ilm_policy_callout/ilm_policy_migration_needed_callout.tsx @@ -19,12 +19,12 @@ import { useInternalApiClient } from '../../lib/reporting_api_client'; const i18nTexts = { title: i18n.translate('xpack.reporting.listing.ilmPolicyCallout.migrationNeededTitle', { - defaultMessage: 'Migrate reporting indices', + defaultMessage: 'Apply new lifecycle policy for reports', }), description: ( {ILM_POLICY_NAME}, }} @@ -33,7 +33,10 @@ const i18nTexts = { buttonLabel: i18n.translate( 'xpack.reporting.listing.ilmPolicyCallout.migrateIndicesButtonLabel', { - defaultMessage: 'Migrate indices', + defaultMessage: 'Apply {ilmPolicyName} policy', + values: { + ilmPolicyName: ILM_POLICY_NAME, + }, } ), migrateErrorTitle: i18n.translate( @@ -45,7 +48,7 @@ const i18nTexts = { migrateSuccessTitle: i18n.translate( 'xpack.reporting.listing.ilmPolicyCallout.migrateIndicesSuccessTitle', { - defaultMessage: 'Successfully migrated reporting indices', + defaultMessage: 'Reporting policy active for all reporting indices', } ), }; diff --git a/x-pack/plugins/reporting/public/management/report_delete_button.tsx b/x-pack/plugins/reporting/public/management/report_delete_button.tsx index dfb411fc195e8..5200191184972 100644 --- a/x-pack/plugins/reporting/public/management/report_delete_button.tsx +++ b/x-pack/plugins/reporting/public/management/report_delete_button.tsx @@ -7,7 +7,8 @@ import { EuiButton, EuiConfirmModal } from '@elastic/eui'; import React, { Fragment, PureComponent } from 'react'; -import { Job, Props as ListingProps } from './report_listing'; +import { Job } from '../lib/job'; +import { Props as ListingProps } from './report_listing'; type DeleteFn = () => Promise; type Props = { jobsToDelete: Job[]; performDelete: DeleteFn } & ListingProps; @@ -46,7 +47,7 @@ export class ReportDeleteButton extends PureComponent { id: 'xpack.reporting.listing.table.deleteConfirmTitle', defaultMessage: `Delete the "{name}" report?`, }, - { name: jobsToDelete[0].object_title } + { name: jobsToDelete[0].title } ); const message = intl.formatMessage({ id: 'xpack.reporting.listing.table.deleteConfirmMessage', diff --git a/x-pack/plugins/reporting/public/management/report_download_button.tsx b/x-pack/plugins/reporting/public/management/report_download_button.tsx index 78022b85e2ff8..b421271037722 100644 --- a/x-pack/plugins/reporting/public/management/report_download_button.tsx +++ b/x-pack/plugins/reporting/public/management/report_download_button.tsx @@ -8,7 +8,8 @@ import { EuiButtonIcon, EuiToolTip } from '@elastic/eui'; import React, { FunctionComponent } from 'react'; import { JOB_STATUSES } from '../../common/constants'; -import { Job as ListingJob, Props as ListingProps } from './report_listing'; +import { Job as ListingJob } from '../lib/job'; +import { Props as ListingProps } from './report_listing'; type Props = { record: ListingJob } & ListingProps; diff --git a/x-pack/plugins/reporting/public/management/report_error_button.tsx b/x-pack/plugins/reporting/public/management/report_error_button.tsx index 0ebdf5ca60b5a..ee0c0e162cb7d 100644 --- a/x-pack/plugins/reporting/public/management/report_error_button.tsx +++ b/x-pack/plugins/reporting/public/management/report_error_button.tsx @@ -9,8 +9,8 @@ import { EuiButtonIcon, EuiCallOut, EuiPopover } from '@elastic/eui'; import { InjectedIntl, injectI18n } from '@kbn/i18n/react'; import React, { Component } from 'react'; import { JOB_STATUSES } from '../../common/constants'; +import { Job as ListingJob } from '../lib/job'; import { JobContent, ReportingAPIClient } from '../lib/reporting_api_client'; -import { Job as ListingJob } from './report_listing'; interface Props { intl: InjectedIntl; @@ -102,7 +102,7 @@ class ReportErrorButtonUi extends Component { this.setState({ isLoading: true }); try { - const reportContent: JobContent = await apiClient.getContent(record.id); + const reportContent: JobContent = await apiClient.getError(record.id); if (this.mounted) { this.setState({ isLoading: false, error: reportContent.content }); } diff --git a/x-pack/plugins/reporting/public/management/report_info_button.tsx b/x-pack/plugins/reporting/public/management/report_info_button.tsx index 719f1ff341daf..92acaa386bd56 100644 --- a/x-pack/plugins/reporting/public/management/report_info_button.tsx +++ b/x-pack/plugins/reporting/public/management/report_info_button.tsx @@ -15,14 +15,16 @@ import { EuiSpacer, EuiText, EuiTitle, + EuiToolTip, } from '@elastic/eui'; -import { get } from 'lodash'; -import React, { Component, Fragment } from 'react'; +import { injectI18n } from '@kbn/i18n/react'; +import React, { Component } from 'react'; import { USES_HEADLESS_JOB_TYPES } from '../../common/constants'; -import { ReportApiJSON } from '../../common/types'; +import { Job } from '../lib/job'; import { ReportingAPIClient } from '../lib/reporting_api_client'; +import { Props as ListingProps } from './report_listing'; -interface Props { +interface Props extends Pick { jobId: string; apiClient: ReportingAPIClient; } @@ -31,23 +33,23 @@ interface State { isLoading: boolean; isFlyoutVisible: boolean; calloutTitle: string; - info: ReportApiJSON | null; + info: Job | null; error: Error | null; } const NA = 'n/a'; const UNKNOWN = 'unknown'; -const getDimensions = (info: ReportApiJSON): string => { +const getDimensions = (info: Job): string => { const defaultDimensions = { width: null, height: null }; - const { width, height } = get(info, 'payload.layout.dimensions', defaultDimensions); + const { width, height } = info.layout?.dimensions || defaultDimensions; if (width && height) { return `Width: ${width} x Height: ${height}`; } - return NA; + return UNKNOWN; }; -export class ReportInfoButton extends Component { +class ReportInfoButtonUi extends Component { private mounted?: boolean; constructor(props: Props) { @@ -75,133 +77,60 @@ export class ReportInfoButton extends Component { } const jobType = info.jobtype || NA; - - interface JobInfo { - title: string; - description: string; - } - - interface JobInfoMap { - [thing: string]: JobInfo[]; - } - const attempts = info.attempts ? info.attempts.toString() : NA; const maxAttempts = info.max_attempts ? info.max_attempts.toString() : NA; const timeout = info.timeout ? info.timeout.toString() : NA; - const warnings = info.output && info.output.warnings ? info.output.warnings.join(',') : null; + const warnings = info.warnings?.join(',') ?? null; + + const jobInfo = [ + { title: 'Title', description: info.title || NA }, + { title: 'Created By', description: info.created_by || NA }, + { title: 'Created At', description: info.created_at || NA }, + { title: 'Timezone', description: info.browserTimezone || NA }, + { title: 'Status', description: info.status || NA }, + ]; - const jobInfoDateTimes: JobInfo[] = [ - { - title: 'Created By', - description: info.created_by || NA, - }, - { - title: 'Created At', - description: info.created_at || NA, - }, - { - title: 'Started At', - description: info.started_at || NA, - }, - { - title: 'Completed At', - description: info.completed_at || NA, - }, + const processingInfo = [ + { title: 'Started At', description: info.started_at || NA }, + { title: 'Completed At', description: info.completed_at || NA }, { title: 'Processed By', description: - info.kibana_name && info.kibana_id ? `${info.kibana_name} (${info.kibana_id})` : UNKNOWN, - }, - { - title: 'Browser Timezone', - description: get(info, 'payload.browserTimezone') || NA, - }, - ]; - const jobInfoPayload: JobInfo[] = [ - { - title: 'Title', - description: get(info, 'payload.title') || NA, - }, - { - title: 'Layout', - description: get(info, 'meta.layout') || NA, - }, - { - title: 'Dimensions', - description: getDimensions(info), - }, - { - title: 'Job Type', - description: jobType, - }, - { - title: 'Content Type', - description: get(info, 'output.content_type') || NA, - }, - { - title: 'Size in Bytes', - description: get(info, 'output.size') || NA, - }, - ]; - const jobInfoStatus: JobInfo[] = [ - { - title: 'Attempts', - description: attempts, - }, - { - title: 'Max Attempts', - description: maxAttempts, - }, - { - title: 'Timeout', - description: timeout, - }, - { - title: 'Status', - description: info.status || NA, - }, - { - title: 'Browser Type', - description: USES_HEADLESS_JOB_TYPES.includes(jobType) ? info.browser_type || UNKNOWN : NA, + info.kibana_name && info.kibana_id ? `${info.kibana_name} (${info.kibana_id})` : NA, }, + { title: 'Content Type', description: info.content_type || NA }, + { title: 'Size in Bytes', description: info.size?.toString() || NA }, + { title: 'Attempts', description: attempts }, + { title: 'Max Attempts', description: maxAttempts }, + { title: 'Timeout', description: timeout }, ]; - if (warnings) { - jobInfoStatus.push({ - title: 'Errors', - description: warnings, - }); - } + const jobScreenshot = [ + { title: 'Dimensions', description: getDimensions(info) }, + { title: 'Layout', description: info.layout?.id || UNKNOWN }, + { title: 'Browser Type', description: info.browser_type || NA }, + ]; - const jobInfoParts: JobInfoMap = { - datetimes: jobInfoDateTimes, - payload: jobInfoPayload, - status: jobInfoStatus, - }; + const warningInfo = warnings && [{ title: 'Errors', description: warnings }]; return ( - - + <> + - - - - + + {USES_HEADLESS_JOB_TYPES.includes(jobType) ? ( + <> + + + + ) : null} + {warningInfo ? ( + <> + + + + ) : null} + ); } @@ -240,23 +169,31 @@ export class ReportInfoButton extends Component { } return ( - - + <> + + + {flyout} - + ); } private loadInfo = async () => { this.setState({ isLoading: true }); try { - const info: ReportApiJSON = await this.props.apiClient.getInfo(this.props.jobId); + const info = await this.props.apiClient.getInfo(this.props.jobId); if (this.mounted) { this.setState({ isLoading: false, info }); } @@ -287,3 +224,5 @@ export class ReportInfoButton extends Component { } }; } + +export const ReportInfoButton = injectI18n(ReportInfoButtonUi); diff --git a/x-pack/plugins/reporting/public/management/report_listing.test.tsx b/x-pack/plugins/reporting/public/management/report_listing.test.tsx index 0b278cbaa0449..0c9b85c2f8cbb 100644 --- a/x-pack/plugins/reporting/public/management/report_listing.test.tsx +++ b/x-pack/plugins/reporting/public/management/report_listing.test.tsx @@ -5,24 +5,20 @@ * 2.0. */ -import React from 'react'; -import { Observable } from 'rxjs'; +import { registerTestBed } from '@kbn/test/jest'; import { UnwrapPromise } from '@kbn/utility-types'; - +import React from 'react'; import { act } from 'react-dom/test-utils'; - -import { registerTestBed } from '@kbn/test/jest'; - -import type { SharePluginSetup, LocatorPublic } from '../../../../../src/plugins/share/public'; +import { Observable } from 'rxjs'; import type { NotificationsSetup } from '../../../../../src/core/public'; import { httpServiceMock, notificationServiceMock } from '../../../../../src/core/public/mocks'; - +import type { LocatorPublic, SharePluginSetup } from '../../../../../src/plugins/share/public'; import type { ILicense } from '../../../licensing/public'; - -import { IlmPolicyMigrationStatus } from '../../common/types'; - -import { ReportingAPIClient, InternalApiClientClientProvider } from '../lib/reporting_api_client'; +import { IlmPolicyMigrationStatus, ReportApiJSON } from '../../common/types'; import { IlmPolicyStatusContextProvider } from '../lib/ilm_policy_status_context'; +import { Job } from '../lib/job'; +import { InternalApiClientClientProvider, ReportingAPIClient } from '../lib/reporting_api_client'; +import { Props, ReportListing } from './report_listing'; jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => { return { @@ -30,21 +26,20 @@ jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => { }; }); -import { ReportListing, Props } from './report_listing'; +const mockJobs: ReportApiJSON[] = [ + { id: 'k90e51pk1ieucbae0c3t8wo2', index: '.reporting-2020.04.12', migration_version: '7.15.0', attempts: 0, browser_type: 'chromium', created_at: '2020-04-14T21:01:13.064Z', created_by: 'elastic', jobtype: 'printable_pdf', max_attempts: 1, meta: { layout: 'preserve_layout', objectType: 'canvas workpad' }, payload: { browserTimezone: 'America/Phoenix', layout: { dimensions: { height: 720, width: 1080 }, id: 'preserve_layout' }, objectType: 'canvas workpad', title: 'My Canvas Workpad' }, process_expiration: '1970-01-01T00:00:00.000Z', status: 'pending', timeout: 300000 }, // prettier-ignore + { id: 'k90e51pk1ieucbae0c3t8wo1', index: '.reporting-2020.04.12', migration_version: '7.15.0', attempts: 1, browser_type: 'chromium', created_at: '2020-04-14T21:01:13.064Z', created_by: 'elastic', jobtype: 'printable_pdf', kibana_id: '5b2de169-2785-441b-ae8c-186a1936b17d', kibana_name: 'spicy.local', max_attempts: 1, meta: { layout: 'preserve_layout', objectType: 'canvas workpad' }, payload: { browserTimezone: 'America/Phoenix', layout: { dimensions: { height: 720, width: 1080 }, id: 'preserve_layout' }, objectType: 'canvas workpad', title: 'My Canvas Workpad' }, process_expiration: '2020-04-14T21:06:14.526Z', started_at: '2020-04-14T21:01:14.526Z', status: 'processing', timeout: 300000 }, + { id: 'k90cmthd1gv8cbae0c2le8bo', index: '.reporting-2020.04.12', migration_version: '7.15.0', attempts: 1, browser_type: 'chromium', completed_at: '2020-04-14T20:19:14.748Z', created_at: '2020-04-14T20:19:02.977Z', created_by: 'elastic', jobtype: 'printable_pdf', kibana_id: '5b2de169-2785-441b-ae8c-186a1936b17d', kibana_name: 'spicy.local', max_attempts: 1, meta: { layout: 'preserve_layout', objectType: 'canvas workpad' }, output: { content_type: 'application/pdf', size: 80262 }, payload: { browserTimezone: 'America/Phoenix', layout: { dimensions: { height: 720, width: 1080 }, id: 'preserve_layout' }, objectType: 'canvas workpad', title: 'My Canvas Workpad' }, process_expiration: '2020-04-14T20:24:04.073Z', started_at: '2020-04-14T20:19:04.073Z', status: 'completed', timeout: 300000 }, + { id: 'k906958e1d4wcbae0c9hip1a', index: '.reporting-2020.04.12', migration_version: '7.15.0', attempts: 1, browser_type: 'chromium', completed_at: '2020-04-14T17:21:08.223Z', created_at: '2020-04-14T17:20:27.326Z', created_by: 'elastic', jobtype: 'printable_pdf', kibana_id: '5b2de169-2785-441b-ae8c-186a1936b17d', kibana_name: 'spicy.local', max_attempts: 1, meta: { layout: 'preserve_layout', objectType: 'canvas workpad' }, output: { content_type: 'application/pdf', size: 49468, warnings: [ 'An error occurred when trying to read the page for visualization panel info. You may need to increase \'xpack.reporting.capture.timeouts.waitForElements\'. TimeoutError: waiting for selector "[data-shared-item],[data-shared-items-count]" failed: timeout 30000ms exceeded', ] }, payload: { browserTimezone: 'America/Phoenix', layout: { dimensions: { height: 720, width: 1080 }, id: 'preserve_layout' }, objectType: 'canvas workpad', title: 'My Canvas Workpad' }, process_expiration: '2020-04-14T17:25:29.444Z', started_at: '2020-04-14T17:20:29.444Z', status: 'completed_with_warnings', timeout: 300000 }, + { id: 'k9067y2a1d4wcbae0cad38n0', index: '.reporting-2020.04.12', migration_version: '7.15.0', attempts: 1, browser_type: 'chromium', completed_at: '2020-04-14T17:19:53.244Z', created_at: '2020-04-14T17:19:31.379Z', created_by: 'elastic', jobtype: 'printable_pdf', kibana_id: '5b2de169-2785-441b-ae8c-186a1936b17d', kibana_name: 'spicy.local', max_attempts: 1, meta: { layout: 'preserve_layout', objectType: 'canvas workpad' }, output: { content_type: 'application/pdf', size: 80262 }, payload: { browserTimezone: 'America/Phoenix', layout: { dimensions: { height: 720, width: 1080 }, id: 'preserve_layout' }, objectType: 'canvas workpad', title: 'My Canvas Workpad' }, process_expiration: '2020-04-14T17:24:39.883Z', started_at: '2020-04-14T17:19:39.883Z', status: 'completed', timeout: 300000 }, + { id: 'k9067s1m1d4wcbae0cdnvcms', index: '.reporting-2020.04.12', migration_version: '7.15.0', attempts: 1, browser_type: 'chromium', completed_at: '2020-04-14T17:19:36.822Z', created_at: '2020-04-14T17:19:23.578Z', created_by: 'elastic', jobtype: 'printable_pdf', kibana_id: '5b2de169-2785-441b-ae8c-186a1936b17d', kibana_name: 'spicy.local', max_attempts: 1, meta: { layout: 'preserve_layout', objectType: 'canvas workpad' }, output: { content_type: 'application/pdf', size: 80262 }, payload: { browserTimezone: 'America/Phoenix', layout: { dimensions: { height: 720, width: 1080 }, id: 'preserve_layout' }, objectType: 'canvas workpad', title: 'My Canvas Workpad' }, process_expiration: '2020-04-14T17:24:25.247Z', started_at: '2020-04-14T17:19:25.247Z', status: 'completed', timeout: 300000 }, + { id: 'k9065q3s1d4wcbae0c00fxlh', index: '.reporting-2020.04.12', migration_version: '7.15.0', attempts: 1, browser_type: 'chromium', completed_at: '2020-04-14T17:18:03.910Z', created_at: '2020-04-14T17:17:47.752Z', created_by: 'elastic', jobtype: 'printable_pdf', kibana_id: '5b2de169-2785-441b-ae8c-186a1936b17d', kibana_name: 'spicy.local', max_attempts: 1, meta: { layout: 'preserve_layout', objectType: 'canvas workpad' }, output: { content_type: 'application/pdf', size: 80262 }, payload: { browserTimezone: 'America/Phoenix', layout: { dimensions: { height: 720, width: 1080 }, id: 'preserve_layout' }, objectType: 'canvas workpad', title: 'My Canvas Workpad' }, process_expiration: '2020-04-14T17:22:50.379Z', started_at: '2020-04-14T17:17:50.379Z', status: 'completed', timeout: 300000 }, + { id: 'k905zdw11d34cbae0c3y6tzh', index: '.reporting-2020.04.12', migration_version: '7.15.0', attempts: 1, browser_type: 'chromium', completed_at: '2020-04-14T17:13:03.719Z', created_at: '2020-04-14T17:12:51.985Z', created_by: 'elastic', jobtype: 'printable_pdf', kibana_id: '5b2de169-2785-441b-ae8c-186a1936b17d', kibana_name: 'spicy.local', max_attempts: 1, meta: { layout: 'preserve_layout', objectType: 'canvas workpad' }, output: { content_type: 'application/pdf', size: 80262 }, payload: { browserTimezone: 'America/Phoenix', layout: { dimensions: { height: 720, width: 1080 }, id: 'preserve_layout' }, objectType: 'canvas workpad', title: 'My Canvas Workpad' }, process_expiration: '2020-04-14T17:17:52.431Z', started_at: '2020-04-14T17:12:52.431Z', status: 'completed', timeout: 300000 }, + { id: 'k8t4ylcb07mi9d006214ifyg', index: '.reporting-2020.04.05', migration_version: '7.15.0', attempts: 1, browser_type: 'chromium', completed_at: '2020-04-09T19:10:10.049Z', created_at: '2020-04-09T19:09:52.139Z', created_by: 'elastic', jobtype: 'PNG', kibana_id: 'f2e59b4e-f79b-4a48-8a7d-6d50a3c1d914', kibana_name: 'spicy.local', max_attempts: 1, meta: { layout: 'png', objectType: 'visualization' }, output: { content_type: 'image/png', size: 123456789 }, payload: { browserTimezone: 'America/Phoenix', layout: { dimensions: { height: 1575, width: 1423 }, id: 'png' }, objectType: 'visualization', title: 'count' }, process_expiration: '2020-04-09T19:14:54.570Z', started_at: '2020-04-09T19:09:54.570Z', status: 'completed', timeout: 300000 }, +]; // prettier-ignore const reportingAPIClient = { - list: () => - Promise.resolve([ - { _id: 'k90e51pk1ieucbae0c3t8wo2', _index: '.reporting-2020.04.12', _score: null, _source: { attempts: 0, browser_type: 'chromium', created_at: '2020-04-14T21:01:13.064Z', created_by: 'elastic', jobtype: 'printable_pdf', max_attempts: 1, meta: { layout: 'preserve_layout', objectType: 'canvas workpad', }, payload: { basePath: '/kbn', browserTimezone: 'America/Phoenix', forceNow: '2020-04-14T21:01:13.062Z', layout: { dimensions: { height: 720, width: 1080, }, id: 'preserve_layout', }, objectType: 'canvas workpad', relativeUrls: [ '/s/hsyjklk/app/canvas#/export/workpad/pdf/workpad-53d38306-eda4-410f-b5e7-efbeca1a8c63/page/1', ], title: 'My Canvas Workpad', }, priority: 10, process_expiration: '1970-01-01T00:00:00.000Z', status: 'pending', timeout: 300000, }, sort: [1586898073064], }, // prettier-ignore - { _id: 'k90e51pk1ieucbae0c3t8wo1', _index: '.reporting-2020.04.12', _score: null, _source: { attempts: 1, browser_type: 'chromium', created_at: '2020-04-14T21:01:13.064Z', created_by: 'elastic', jobtype: 'printable_pdf', kibana_id: '5b2de169-2785-441b-ae8c-186a1936b17d', kibana_name: 'spicy.local', max_attempts: 1, meta: { layout: 'preserve_layout', objectType: 'canvas workpad', }, payload: { basePath: '/kbn', browserTimezone: 'America/Phoenix', forceNow: '2020-04-14T21:01:13.062Z', layout: { dimensions: { height: 720, width: 1080, }, id: 'preserve_layout', }, objectType: 'canvas workpad', relativeUrls: [ '/s/hsyjklk/app/canvas#/export/workpad/pdf/workpad-53d38306-eda4-410f-b5e7-efbeca1a8c63/page/1', ], title: 'My Canvas Workpad', }, priority: 10, process_expiration: '2020-04-14T21:06:14.526Z', started_at: '2020-04-14T21:01:14.526Z', status: 'processing', timeout: 300000, }, sort: [1586898073064], }, - { _id: 'k90cmthd1gv8cbae0c2le8bo', _index: '.reporting-2020.04.12', _score: null, _source: { attempts: 1, browser_type: 'chromium', completed_at: '2020-04-14T20:19:14.748Z', created_at: '2020-04-14T20:19:02.977Z', created_by: 'elastic', jobtype: 'printable_pdf', kibana_id: '5b2de169-2785-441b-ae8c-186a1936b17d', kibana_name: 'spicy.local', max_attempts: 1, meta: { layout: 'preserve_layout', objectType: 'canvas workpad', }, output: { content_type: 'application/pdf', size: 80262, }, payload: { basePath: '/kbn', browserTimezone: 'America/Phoenix', forceNow: '2020-04-14T20:19:02.976Z', layout: { dimensions: { height: 720, width: 1080, }, id: 'preserve_layout', }, objectType: 'canvas workpad', relativeUrls: [ '/s/hsyjklk/app/canvas#/export/workpad/pdf/workpad-53d38306-eda4-410f-b5e7-efbeca1a8c63/page/1', ], title: 'My Canvas Workpad', }, priority: 10, process_expiration: '2020-04-14T20:24:04.073Z', started_at: '2020-04-14T20:19:04.073Z', status: 'completed', timeout: 300000, }, sort: [1586895542977], }, - { _id: 'k906958e1d4wcbae0c9hip1a', _index: '.reporting-2020.04.12', _score: null, _source: { attempts: 1, browser_type: 'chromium', completed_at: '2020-04-14T17:21:08.223Z', created_at: '2020-04-14T17:20:27.326Z', created_by: 'elastic', jobtype: 'printable_pdf', kibana_id: '5b2de169-2785-441b-ae8c-186a1936b17d', kibana_name: 'spicy.local', max_attempts: 1, meta: { layout: 'preserve_layout', objectType: 'canvas workpad', }, output: { content_type: 'application/pdf', size: 49468, warnings: [ 'An error occurred when trying to read the page for visualization panel info. You may need to increase \'xpack.reporting.capture.timeouts.waitForElements\'. TimeoutError: waiting for selector "[data-shared-item],[data-shared-items-count]" failed: timeout 30000ms exceeded', ], }, payload: { basePath: '/kbn', browserTimezone: 'America/Phoenix', forceNow: '2020-04-14T17:20:27.326Z', layout: { dimensions: { height: 720, width: 1080, }, id: 'preserve_layout', }, objectType: 'canvas workpad', relativeUrls: [ '/s/hsyjklk/app/canvas#/export/workpad/pdf/workpad-53d38306-eda4-410f-b5e8-efbeca1a8c63/page/1', ], title: 'My Canvas Workpad', }, priority: 10, process_expiration: '2020-04-14T17:25:29.444Z', started_at: '2020-04-14T17:20:29.444Z', status: 'completed_with_warnings', timeout: 300000, }, sort: [1586884827326], }, - { _id: 'k9067y2a1d4wcbae0cad38n0', _index: '.reporting-2020.04.12', _score: null, _source: { attempts: 1, browser_type: 'chromium', completed_at: '2020-04-14T17:19:53.244Z', created_at: '2020-04-14T17:19:31.379Z', created_by: 'elastic', jobtype: 'printable_pdf', kibana_id: '5b2de169-2785-441b-ae8c-186a1936b17d', kibana_name: 'spicy.local', max_attempts: 1, meta: { layout: 'preserve_layout', objectType: 'canvas workpad', }, output: { content_type: 'application/pdf', size: 80262, }, payload: { basePath: '/kbn', browserTimezone: 'America/Phoenix', forceNow: '2020-04-14T17:19:31.378Z', layout: { dimensions: { height: 720, width: 1080, }, id: 'preserve_layout', }, objectType: 'canvas workpad', relativeUrls: [ '/s/hsyjklk/app/canvas#/export/workpad/pdf/workpad-53d38306-eda4-410f-b5e7-efbeca1a8c63/page/1', ], title: 'My Canvas Workpad', }, priority: 10, process_expiration: '2020-04-14T17:24:39.883Z', started_at: '2020-04-14T17:19:39.883Z', status: 'completed', timeout: 300000, }, sort: [1586884771379], }, - { _id: 'k9067s1m1d4wcbae0cdnvcms', _index: '.reporting-2020.04.12', _score: null, _source: { attempts: 1, browser_type: 'chromium', completed_at: '2020-04-14T17:19:36.822Z', created_at: '2020-04-14T17:19:23.578Z', created_by: 'elastic', jobtype: 'printable_pdf', kibana_id: '5b2de169-2785-441b-ae8c-186a1936b17d', kibana_name: 'spicy.local', max_attempts: 1, meta: { layout: 'preserve_layout', objectType: 'canvas workpad', }, output: { content_type: 'application/pdf', size: 80262, }, payload: { basePath: '/kbn', browserTimezone: 'America/Phoenix', forceNow: '2020-04-14T17:19:23.578Z', layout: { dimensions: { height: 720, width: 1080, }, id: 'preserve_layout', }, objectType: 'canvas workpad', relativeUrls: [ '/s/hsyjklk/app/canvas#/export/workpad/pdf/workpad-53d38306-eda4-410f-b5e7-efbeca1a8c63/page/1', ], title: 'My Canvas Workpad', }, priority: 10, process_expiration: '2020-04-14T17:24:25.247Z', started_at: '2020-04-14T17:19:25.247Z', status: 'completed', timeout: 300000, }, sort: [1586884763578], }, - { _id: 'k9065q3s1d4wcbae0c00fxlh', _index: '.reporting-2020.04.12', _score: null, _source: { attempts: 1, browser_type: 'chromium', completed_at: '2020-04-14T17:18:03.910Z', created_at: '2020-04-14T17:17:47.752Z', created_by: 'elastic', jobtype: 'printable_pdf', kibana_id: '5b2de169-2785-441b-ae8c-186a1936b17d', kibana_name: 'spicy.local', max_attempts: 1, meta: { layout: 'preserve_layout', objectType: 'canvas workpad', }, output: { content_type: 'application/pdf', size: 80262, }, payload: { basePath: '/kbn', browserTimezone: 'America/Phoenix', forceNow: '2020-04-14T17:17:47.750Z', layout: { dimensions: { height: 720, width: 1080, }, id: 'preserve_layout', }, objectType: 'canvas workpad', relativeUrls: [ '/s/hsyjklk/app/canvas#/export/workpad/pdf/workpad-53d38306-eda4-410f-b5e7-efbeca1a8c63/page/1', ], title: 'My Canvas Workpad', }, priority: 10, process_expiration: '2020-04-14T17:22:50.379Z', started_at: '2020-04-14T17:17:50.379Z', status: 'completed', timeout: 300000, }, sort: [1586884667752], }, - { _id: 'k905zdw11d34cbae0c3y6tzh', _index: '.reporting-2020.04.12', _score: null, _source: { attempts: 1, browser_type: 'chromium', completed_at: '2020-04-14T17:13:03.719Z', created_at: '2020-04-14T17:12:51.985Z', created_by: 'elastic', jobtype: 'printable_pdf', kibana_id: '5b2de169-2785-441b-ae8c-186a1936b17d', kibana_name: 'spicy.local', max_attempts: 1, meta: { layout: 'preserve_layout', objectType: 'canvas workpad', }, output: { content_type: 'application/pdf', size: 80262, }, payload: { basePath: '/kbn', browserTimezone: 'America/Phoenix', forceNow: '2020-04-14T17:12:51.984Z', layout: { dimensions: { height: 720, width: 1080, }, id: 'preserve_layout', }, objectType: 'canvas workpad', relativeUrls: [ '/s/hsyjklk/app/canvas#/export/workpad/pdf/workpad-53d38306-eda4-410f-b5e7-efbeca1a8c63/page/1', ], title: 'My Canvas Workpad', }, priority: 10, process_expiration: '2020-04-14T17:17:52.431Z', started_at: '2020-04-14T17:12:52.431Z', status: 'completed', timeout: 300000, }, sort: [1586884371985], }, - { _id: 'k8t4ylcb07mi9d006214ifyg', _index: '.reporting-2020.04.05', _score: null, _source: { attempts: 1, browser_type: 'chromium', completed_at: '2020-04-09T19:10:10.049Z', created_at: '2020-04-09T19:09:52.139Z', created_by: 'elastic', jobtype: 'PNG', kibana_id: 'f2e59b4e-f79b-4a48-8a7d-6d50a3c1d914', kibana_name: 'spicy.local', max_attempts: 1, meta: { layout: 'png', objectType: 'visualization', }, output: { content_type: 'image/png', }, payload: { basePath: '/kbn', browserTimezone: 'America/Phoenix', forceNow: '2020-04-09T19:09:52.137Z', layout: { dimensions: { height: 1575, width: 1423, }, id: 'png', }, objectType: 'visualization', relativeUrl: "/s/hsyjklk/app/visualize#/edit/94d1fe40-7a94-11ea-b373-0749f92ad295?_a=(filters:!(),linked:!f,query:(language:kuery,query:''),uiState:(),vis:(aggs:!((enabled:!t,id:'1',params:(),schema:metric,type:count)),params:(addLegend:!f,addTooltip:!t,metric:(colorSchema:'Green%20to%20Red',colorsRange:!((from:0,to:10000)),invertColors:!f,labels:(show:!t),metricColorMode:None,percentageMode:!f,style:(bgColor:!f,bgFill:%23000,fontSize:60,labelColor:!f,subText:''),useRanges:!f),type:metric),title:count,type:metric))&_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:now-15y,to:now))&indexPattern=d81752b0-7434-11ea-be36-1f978cda44d4&type=metric", title: 'count', }, priority: 10, process_expiration: '2020-04-09T19:14:54.570Z', started_at: '2020-04-09T19:09:54.570Z', status: 'completed', timeout: 300000, }, sort: [1586459392139], }, - ]), // prettier-ignore + list: () => Promise.resolve(mockJobs.map((j) => new Job(j))), total: () => Promise.resolve(18), migrateReportingIndicesIlmPolicy: jest.fn(), } as any; diff --git a/x-pack/plugins/reporting/public/management/report_listing.tsx b/x-pack/plugins/reporting/public/management/report_listing.tsx index dd41314b4883f..30c9325a0f34f 100644 --- a/x-pack/plugins/reporting/public/management/report_listing.tsx +++ b/x-pack/plugins/reporting/public/management/report_listing.tsx @@ -9,15 +9,14 @@ import { EuiBasicTable, EuiFlexGroup, EuiFlexItem, + EuiLoadingSpinner, EuiPageHeader, EuiSpacer, EuiText, EuiTextColor, - EuiLoadingSpinner, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; -import { get } from 'lodash'; import moment from 'moment'; import { Component, default as React, Fragment } from 'react'; import { Subscription } from 'rxjs'; @@ -26,37 +25,16 @@ import { ILicense, LicensingPluginSetup } from '../../../licensing/public'; import { JOB_STATUSES as JobStatuses } from '../../common/constants'; import { Poller } from '../../common/poller'; import { durationToNumber } from '../../common/schema_utils'; -import { checkLicense } from '../lib/license_check'; -import { - JobQueueEntry, - ReportingAPIClient, - useInternalApiClient, -} from '../lib/reporting_api_client'; import { useIlmPolicyStatus, UseIlmPolicyStatusReturn } from '../lib/ilm_policy_status_context'; -import type { SharePluginSetup } from '../shared_imports'; +import { Job } from '../lib/job'; +import { checkLicense } from '../lib/license_check'; +import { ReportingAPIClient, useInternalApiClient } from '../lib/reporting_api_client'; import { ClientConfigType } from '../plugin'; +import type { SharePluginSetup } from '../shared_imports'; import { ReportDeleteButton, ReportDownloadButton, ReportErrorButton, ReportInfoButton } from './'; -import { ReportDiagnostic } from './report_diagnostic'; -import { MigrateIlmPolicyCallOut } from './migrate_ilm_policy_callout'; import { IlmPolicyLink } from './ilm_policy_link'; - -export interface Job { - id: string; - type: string; - object_type: string; - object_title: string; - created_by?: string | false; - created_at: string; - started_at?: string; - completed_at?: string; - status: string; - statusLabel: string; - max_size_reached?: boolean; - attempts: number; - max_attempts: number; - csv_contains_formulas: boolean; - warnings?: string[]; -} +import { MigrateIlmPolicyCallOut } from './migrate_ilm_policy_callout'; +import { ReportDiagnostic } from './report_diagnostic'; export interface Props { intl: InjectedIntl; @@ -251,7 +229,7 @@ class ReportListingUi extends Component { id: 'xpack.reporting.listing.table.deleteConfim', defaultMessage: `The {reportTitle} report was deleted`, }, - { reportTitle: record.object_title } + { reportTitle: record.title } ) ); } catch (error) { @@ -293,7 +271,7 @@ class ReportListingUi extends Component { this.setState(() => ({ isLoading: true })); } - let jobs: JobQueueEntry[]; + let jobs: Job[]; let total: number; try { jobs = await this.props.apiClient.list(this.state.page); @@ -325,28 +303,7 @@ class ReportListingUi extends Component { this.setState(() => ({ isLoading: false, total, - jobs: jobs.map( - (job: JobQueueEntry): Job => { - const { _source: source } = job; - return { - id: job._id, - type: source.jobtype, - object_type: source.payload.objectType, - object_title: source.payload.title, - created_by: source.created_by, - created_at: source.created_at, - started_at: source.started_at, - completed_at: source.completed_at, - status: source.status, - statusLabel: jobStatusLabelsMap.get(source.status as JobStatuses) || source.status, - max_size_reached: source.output ? source.output.max_size_reached : false, - attempts: source.attempts, - max_attempts: source.max_attempts, - csv_contains_formulas: get(source, 'output.csv_contains_formulas'), - warnings: source.output ? source.output.warnings : undefined, - }; - } - ), + jobs, })); } }; @@ -369,7 +326,7 @@ class ReportListingUi extends Component { const tableColumns = [ { - field: 'object_title', + field: 'title', name: intl.formatMessage({ id: 'xpack.reporting.listing.tableColumns.reportTitle', defaultMessage: 'Report', @@ -379,7 +336,7 @@ class ReportListingUi extends Component {
{objectTitle}
- {record.object_type} + {record.objectType}
); diff --git a/x-pack/plugins/reporting/server/lib/enqueue_job.ts b/x-pack/plugins/reporting/server/lib/enqueue_job.ts index 70492b415f961..ec2e443d86c80 100644 --- a/x-pack/plugins/reporting/server/lib/enqueue_job.ts +++ b/x-pack/plugins/reporting/server/lib/enqueue_job.ts @@ -47,7 +47,6 @@ export function enqueueJobFactory( reporting.getStore(), ]); - const config = reporting.getConfig(); const job = await createJob!(jobParams, context, request); // 1. Add the report to ReportingStore to show as pending @@ -55,7 +54,6 @@ export function enqueueJobFactory( new Report({ jobtype: exportType.jobType, created_by: user ? user.username : false, - max_attempts: config.get('capture', 'maxAttempts'), // NOTE: since max attempts is stored in the document, changing the capture.maxAttempts setting does not affect existing pending reports payload: job, meta: { objectType: jobParams.objectType, diff --git a/x-pack/plugins/reporting/server/lib/store/report.test.ts b/x-pack/plugins/reporting/server/lib/store/report.test.ts index a8d14e12a738b..4bc45fd745a56 100644 --- a/x-pack/plugins/reporting/server/lib/store/report.test.ts +++ b/x-pack/plugins/reporting/server/lib/store/report.test.ts @@ -48,7 +48,7 @@ describe('Class Report', () => { index: '.reporting-test-index-12345', jobtype: 'test-report', max_attempts: 50, - payload: { headers: 'payload_test_field', objectType: 'testOt' }, + payload: { objectType: 'testOt' }, meta: { objectType: 'test' }, status: 'pending', timeout: 30000, @@ -109,7 +109,7 @@ describe('Class Report', () => { jobtype: 'test-report', max_attempts: 50, meta: { objectType: 'stange' }, - payload: { headers: 'payload_test_field', objectType: 'testOt' }, + payload: { objectType: 'testOt' }, started_at: undefined, status: 'pending', timeout: 30000, @@ -117,7 +117,7 @@ describe('Class Report', () => { }); it('throws error if converted to task JSON before being synced with ES storage', () => { - const report = new Report({} as any); + const report = new Report({ jobtype: 'spam', payload: {} } as any); expect(() => report.updateWithEsDoc(report)).toThrowErrorMatchingInlineSnapshot( `"Report object from ES has missing fields!"` ); diff --git a/x-pack/plugins/reporting/server/lib/store/report.ts b/x-pack/plugins/reporting/server/lib/store/report.ts index fa5b91527ccc4..0f970ead7c75c 100644 --- a/x-pack/plugins/reporting/server/lib/store/report.ts +++ b/x-pack/plugins/reporting/server/lib/store/report.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { omit } from 'lodash'; import moment from 'moment'; // @ts-ignore no module definition import Puid from 'puid'; @@ -33,23 +34,25 @@ export class Report implements Partial { public _primary_term?: number; // set by ES public _seq_no?: number; // set by ES - public readonly kibana_name: ReportSource['kibana_name']; - public readonly kibana_id: ReportSource['kibana_id']; public readonly jobtype: ReportSource['jobtype']; public readonly created_at: ReportSource['created_at']; public readonly created_by: ReportSource['created_by']; public readonly payload: ReportSource['payload']; public readonly meta: ReportSource['meta']; - public readonly max_attempts: ReportSource['max_attempts']; - public readonly browser_type?: ReportSource['browser_type']; + public readonly browser_type: ReportSource['browser_type']; public readonly status: ReportSource['status']; public readonly attempts: ReportSource['attempts']; - public readonly output?: ReportSource['output']; - public readonly started_at?: ReportSource['started_at']; - public readonly completed_at?: ReportSource['completed_at']; - public readonly timeout?: ReportSource['timeout']; + + // fields with undefined values exist in report jobs that have not been claimed + public readonly kibana_name: ReportSource['kibana_name']; + public readonly kibana_id: ReportSource['kibana_id']; + public readonly output: ReportSource['output']; + public readonly started_at: ReportSource['started_at']; + public readonly completed_at: ReportSource['completed_at']; + public readonly timeout: ReportSource['timeout']; + public readonly max_attempts: ReportSource['max_attempts']; public process_expiration?: ReportSource['process_expiration']; public migration_version: string; @@ -66,20 +69,29 @@ export class Report implements Partial { this.migration_version = MIGRATION_VERSION; - this.payload = opts.payload!; - this.kibana_name = opts.kibana_name!; - this.kibana_id = opts.kibana_id!; - this.jobtype = opts.jobtype!; - this.max_attempts = opts.max_attempts!; - this.attempts = opts.attempts || 0; + // see enqueue_job for all the fields that are expected to exist when adding a report + if (opts.jobtype == null) { + throw new Error(`jobtype is expected!`); + } + if (opts.payload == null) { + throw new Error(`payload is expected!`); + } - this.process_expiration = opts.process_expiration; + this.payload = opts.payload; + this.kibana_id = opts.kibana_id; + this.kibana_name = opts.kibana_name; + this.jobtype = opts.jobtype; + this.max_attempts = opts.max_attempts; + this.attempts = opts.attempts || 0; this.timeout = opts.timeout; + this.browser_type = opts.browser_type; + this.process_expiration = opts.process_expiration; + this.started_at = opts.started_at; + this.completed_at = opts.completed_at; this.created_at = opts.created_at || moment.utc().toISOString(); this.created_by = opts.created_by || false; this.meta = opts.meta || { objectType: 'unknown' }; - this.browser_type = opts.browser_type; this.status = opts.status || JOB_STATUSES.PENDING; this.output = opts.output || null; @@ -113,9 +125,9 @@ export class Report implements Partial { created_by: this.created_by, payload: this.payload, meta: this.meta, - timeout: this.timeout!, + timeout: this.timeout, max_attempts: this.max_attempts, - browser_type: this.browser_type!, + browser_type: this.browser_type, status: this.status, attempts: this.attempts, started_at: this.started_at, @@ -142,7 +154,6 @@ export class Report implements Partial { payload: this.payload, meta: this.meta, attempts: this.attempts, - max_attempts: this.max_attempts, }; } @@ -158,7 +169,6 @@ export class Report implements Partial { jobtype: this.jobtype, created_at: this.created_at, created_by: this.created_by, - payload: this.payload, meta: this.meta, timeout: this.timeout, max_attempts: this.max_attempts, @@ -167,6 +177,9 @@ export class Report implements Partial { attempts: this.attempts, started_at: this.started_at, completed_at: this.completed_at, + migration_version: this.migration_version, + payload: omit(this.payload, 'headers'), + output: omit(this.output, 'content'), }; } } diff --git a/x-pack/plugins/reporting/server/lib/store/store.ts b/x-pack/plugins/reporting/server/lib/store/store.ts index 7a7dd20e1b25c..40a73f294c5a9 100644 --- a/x-pack/plugins/reporting/server/lib/store/store.ts +++ b/x-pack/plugins/reporting/server/lib/store/store.ts @@ -29,6 +29,7 @@ export type ReportProcessingFields = Required<{ browser_type: Report['browser_type']; attempts: Report['attempts']; started_at: Report['started_at']; + max_attempts: Report['max_attempts']; timeout: Report['timeout']; process_expiration: Report['process_expiration']; }>; diff --git a/x-pack/plugins/reporting/server/lib/tasks/execute_report.ts b/x-pack/plugins/reporting/server/lib/tasks/execute_report.ts index f9e2cd82b0805..a52f452436d6d 100644 --- a/x-pack/plugins/reporting/server/lib/tasks/execute_report.ts +++ b/x-pack/plugins/reporting/server/lib/tasks/execute_report.ts @@ -135,9 +135,8 @@ export class ExecuteReportTask implements ReportingTask { const m = moment(); - // check if job has exceeded maxAttempts (stored in job params) and somehow hasn't been marked as failed yet - // NOTE: the max attempts value comes from the stored document, so changing the capture.maxAttempts config setting does not affect existing pending reports - const maxAttempts = task.max_attempts; + // check if job has exceeded the configured maxAttempts + const maxAttempts = this.config.capture.maxAttempts; if (report.attempts >= maxAttempts) { const err = new Error(`Max attempts reached (${maxAttempts}). Queue timeout reached.`); await this._failJob(report, err); @@ -153,6 +152,7 @@ export class ExecuteReportTask implements ReportingTask { kibana_name: this.kibanaName, browser_type: this.config.capture.browser.type, attempts: report.attempts + 1, + max_attempts: maxAttempts, started_at: startTime, timeout: queueTimeout, process_expiration: expirationTime, @@ -195,7 +195,7 @@ export class ExecuteReportTask implements ReportingTask { const completedTime = moment().toISOString(); const doc: ReportFailedFields = { completed_at: completedTime, - output: docOutput, + output: docOutput ?? null, }; return await store.setReportFailed(report, doc); @@ -306,11 +306,14 @@ export class ExecuteReportTask implements ReportingTask { } if (!report) { + this.reporting.untrackReport(jobId); errorLogger(this.logger, `Job ${jobId} could not be claimed. Exiting...`); return; } - const { jobtype: jobType, attempts, max_attempts: maxAttempts } = report; + const { jobtype: jobType, attempts } = report; + const maxAttempts = this.config.capture.maxAttempts; + this.logger.debug( `Starting ${jobType} report ${jobId}: attempt ${attempts} of ${maxAttempts}.` ); diff --git a/x-pack/plugins/reporting/server/lib/tasks/index.ts b/x-pack/plugins/reporting/server/lib/tasks/index.ts index c02b06d97adc7..662528124e6c0 100644 --- a/x-pack/plugins/reporting/server/lib/tasks/index.ts +++ b/x-pack/plugins/reporting/server/lib/tasks/index.ts @@ -27,7 +27,6 @@ export interface ReportTaskParams { created_at: ReportSource['created_at']; created_by: ReportSource['created_by']; jobtype: ReportSource['jobtype']; - max_attempts: ReportSource['max_attempts']; attempts: ReportSource['attempts']; meta: ReportSource['meta']; } diff --git a/x-pack/plugins/reporting/server/routes/jobs.test.ts b/x-pack/plugins/reporting/server/routes/jobs.test.ts index 3f913dfd1f32f..3040ea351f7d0 100644 --- a/x-pack/plugins/reporting/server/routes/jobs.test.ts +++ b/x-pack/plugins/reporting/server/routes/jobs.test.ts @@ -152,7 +152,10 @@ describe('GET /api/reporting/jobs/download', () => { it('returns a 401 if not a valid job type', async () => { mockEsClient.search.mockResolvedValueOnce({ - body: getHits({ jobtype: 'invalidJobType' }), + body: getHits({ + jobtype: 'invalidJobType', + payload: { title: 'invalid!' }, + }), } as any); registerJobInfoRoutes(core); @@ -163,7 +166,11 @@ describe('GET /api/reporting/jobs/download', () => { it('when a job is incomplete', async () => { mockEsClient.search.mockResolvedValueOnce({ - body: getHits({ jobtype: 'unencodedJobType', status: 'pending' }), + body: getHits({ + jobtype: 'unencodedJobType', + status: 'pending', + payload: { title: 'incomplete!' }, + }), } as any); registerJobInfoRoutes(core); @@ -182,6 +189,7 @@ describe('GET /api/reporting/jobs/download', () => { jobtype: 'unencodedJobType', status: 'failed', output: { content: 'job failure message' }, + payload: { title: 'failing job!' }, }), } as any); registerJobInfoRoutes(core); @@ -207,9 +215,7 @@ describe('GET /api/reporting/jobs/download', () => { jobtype: jobType, status: 'completed', output: { content: outputContent, content_type: outputContentType }, - payload: { - title, - }, + payload: { title }, }); }; diff --git a/x-pack/plugins/reporting/server/routes/jobs.ts b/x-pack/plugins/reporting/server/routes/jobs.ts index 3f2a95a34224c..0d0332983d6bc 100644 --- a/x-pack/plugins/reporting/server/routes/jobs.ts +++ b/x-pack/plugins/reporting/server/routes/jobs.ts @@ -71,7 +71,7 @@ export function registerJobInfoRoutes(reporting: ReportingCore) { path: `${MAIN_ENTRY}/count`, validate: false, }, - userHandler(async (user, context, req, res) => { + userHandler(async (user, context, _req, res) => { // ensure the async dependencies are loaded if (!context.reporting) { return handleUnavailable(res); @@ -115,22 +115,20 @@ export function registerJobInfoRoutes(reporting: ReportingCore) { } = await reporting.getLicenseInfo(); const jobsQuery = jobsQueryFactory(reporting); - const result = await jobsQuery.get(user, docId, { includeContent: true }); + const result = await jobsQuery.getContent(user, docId); if (!result) { throw Boom.notFound(); } - const { - _source: { jobtype: jobType, output: jobOutput }, - } = result; + const { jobtype: jobType, output } = result; if (!jobTypes.includes(jobType)) { throw Boom.unauthorized(`Sorry, you are not authorized to download ${jobType} reports`); } return res.ok({ - body: jobOutput || {}, + body: output?.content ?? {}, headers: { 'content-type': 'application/json', }, @@ -166,21 +164,14 @@ export function registerJobInfoRoutes(reporting: ReportingCore) { throw Boom.notFound(); } - const { _source: job } = result; - const { jobtype: jobType, payload: jobPayload } = job; + const { jobtype: jobType } = result; if (!jobTypes.includes(jobType)) { throw Boom.unauthorized(`Sorry, you are not authorized to view ${jobType} info`); } return res.ok({ - body: { - ...job, - payload: { - ...jobPayload, - headers: undefined, - }, - }, + body: result, headers: { 'content-type': 'application/json', }, diff --git a/x-pack/plugins/reporting/server/routes/lib/get_document_payload.ts b/x-pack/plugins/reporting/server/routes/lib/get_document_payload.ts index 4e8e888e4e266..2141252c70bfa 100644 --- a/x-pack/plugins/reporting/server/routes/lib/get_document_payload.ts +++ b/x-pack/plugins/reporting/server/routes/lib/get_document_payload.ts @@ -7,12 +7,11 @@ // @ts-ignore import contentDisposition from 'content-disposition'; -import { get } from 'lodash'; import { CSV_JOB_TYPE, CSV_JOB_TYPE_DEPRECATED } from '../../../common/constants'; import { ExportTypesRegistry, statuses } from '../../lib'; -import { ReportDocument } from '../../lib/store'; import { TaskRunResult } from '../../lib/tasks'; import { ExportTypeDefinition } from '../../types'; +import { ReportContent } from './jobs_query'; export interface ErrorFromPayload { message: string; @@ -35,8 +34,8 @@ const getReportingHeaders = (output: TaskRunResult, exportType: ExportTypeDefini const metaDataHeaders: Record = {}; if (exportType.jobType === CSV_JOB_TYPE || exportType.jobType === CSV_JOB_TYPE_DEPRECATED) { - const csvContainsFormulas = get(output, 'csv_contains_formulas', false); - const maxSizedReach = get(output, 'max_size_reached', false); + const csvContainsFormulas = output.csv_contains_formulas ?? false; + const maxSizedReach = output.max_size_reached ?? false; metaDataHeaders['kbn-csv-contains-formulas'] = csvContainsFormulas; metaDataHeaders['kbn-max-size-reached'] = maxSizedReach; @@ -98,10 +97,12 @@ export function getDocumentPayloadFactory(exportTypesRegistry: ExportTypesRegist }; } - return function getDocumentPayload(doc: ReportDocument): Payload { - const { status, jobtype: jobType, payload: { title } = { title: '' } } = doc._source; - const { output } = doc._source; - + return function getDocumentPayload({ + status, + jobtype: jobType, + payload: { title } = { title: 'unknown' }, + output, + }: ReportContent): Payload { if (output) { if (status === statuses.JOB_STATUS_COMPLETED || status === statuses.JOB_STATUS_WARNINGS) { return getCompleted(output, jobType, title); diff --git a/x-pack/plugins/reporting/server/routes/lib/job_response_handler.ts b/x-pack/plugins/reporting/server/routes/lib/job_response_handler.ts index 8ffefa9c8a98c..f9519f74060f9 100644 --- a/x-pack/plugins/reporting/server/routes/lib/job_response_handler.ts +++ b/x-pack/plugins/reporting/server/routes/lib/job_response_handler.ts @@ -35,16 +35,14 @@ export function downloadJobResponseHandlerFactory(reporting: ReportingCore) { try { const { docId } = params; - const doc = await jobsQuery.get(user, docId, { includeContent: !opts.excludeContent }); + const doc = await jobsQuery.getContent(user, docId); if (!doc) { return res.notFound(); } - const { jobtype: jobType } = doc._source; - - if (!validJobTypes.includes(jobType)) { + if (!validJobTypes.includes(doc.jobtype)) { return res.unauthorized({ - body: `Sorry, you are not authorized to download ${jobType} reports`, + body: `Sorry, you are not authorized to download ${doc.jobtype} reports`, }); } @@ -81,13 +79,13 @@ export function deleteJobResponseHandlerFactory(reporting: ReportingCore) { params: JobResponseHandlerParams ) { const { docId } = params; - const doc = await jobsQuery.get(user, docId, { includeContent: false }); + const doc = await jobsQuery.get(user, docId); if (!doc) { return res.notFound(); } - const { jobtype: jobType } = doc._source; + const { jobtype: jobType } = doc; if (!validJobTypes.includes(jobType)) { return res.unauthorized({ @@ -96,7 +94,7 @@ export function deleteJobResponseHandlerFactory(reporting: ReportingCore) { } try { - const docIndex = doc._index; + const docIndex = doc.index; await jobsQuery.delete(docIndex, docId); return res.ok({ body: { deleted: true }, diff --git a/x-pack/plugins/reporting/server/routes/lib/jobs_query.ts b/x-pack/plugins/reporting/server/routes/lib/jobs_query.ts index 2fec34470ff1f..76896a7472d59 100644 --- a/x-pack/plugins/reporting/server/routes/lib/jobs_query.ts +++ b/x-pack/plugins/reporting/server/routes/lib/jobs_query.ts @@ -5,20 +5,19 @@ * 2.0. */ +import { ApiResponse } from '@elastic/elasticsearch'; +import { DeleteResponse, SearchHit, SearchResponse } from '@elastic/elasticsearch/api/types'; import { ResponseError } from '@elastic/elasticsearch/lib/errors'; import { i18n } from '@kbn/i18n'; import { UnwrapPromise } from '@kbn/utility-types'; import { ElasticsearchClient } from 'src/core/server'; import { ReportingCore } from '../../'; -import { ReportDocument } from '../../lib/store'; +import { ReportApiJSON, ReportDocument, ReportSource } from '../../../common/types'; +import { Report } from '../../lib/store'; import { ReportingUser } from '../../types'; type SearchRequest = Required>[0]; -interface GetOpts { - includeContent?: boolean; -} - const defaultSize = 10; const getUsername = (user: ReportingUser) => (user ? user.username : false); @@ -33,7 +32,25 @@ function getSearchBody(body: SearchRequest['body']): SearchRequest['body'] { }; } -export function jobsQueryFactory(reportingCore: ReportingCore) { +export type ReportContent = Pick & { + payload?: Pick; +}; + +interface JobsQueryFactory { + list( + jobTypes: string[], + user: ReportingUser, + page: number, + size: number, + jobIds: string[] | null + ): Promise; + count(jobTypes: string[], user: ReportingUser): Promise; + get(user: ReportingUser, id: string): Promise; + getContent(user: ReportingUser, id: string): Promise; + delete(deleteIndex: string, id: string): Promise>; +} + +export function jobsQueryFactory(reportingCore: ReportingCore): JobsQueryFactory { function getIndex() { const config = reportingCore.getConfig(); @@ -57,13 +74,7 @@ export function jobsQueryFactory(reportingCore: ReportingCore) { } return { - async list( - jobTypes: string[], - user: ReportingUser, - page = 0, - size = defaultSize, - jobIds: string[] | null - ) { + async list(jobTypes, user, page = 0, size = defaultSize, jobIds) { const username = getUsername(user); const body = getSearchBody({ size, @@ -83,15 +94,23 @@ export function jobsQueryFactory(reportingCore: ReportingCore) { }, }); - const response = await execQuery((elasticsearchClient) => + const response = (await execQuery((elasticsearchClient) => elasticsearchClient.search({ body, index: getIndex() }) + )) as ApiResponse>; + + return ( + response?.body.hits?.hits.map((report: SearchHit) => { + const { _source: reportSource, ...reportHead } = report; + if (reportSource) { + const reportInstance = new Report({ ...reportSource, ...reportHead }); + return reportInstance.toApiJSON(); + } + throw new Error(`Search hit did not include _source!`); + }) ?? [] ); - - // FIXME: return the info in ReportApiJSON format; - return response?.body.hits?.hits ?? []; }, - async count(jobTypes: string[], user: ReportingUser) { + async count(jobTypes, user) { const username = getUsername(user); const body = { query: { @@ -112,14 +131,50 @@ export function jobsQueryFactory(reportingCore: ReportingCore) { return response?.body.count ?? 0; }, - async get(user: ReportingUser, id: string, opts: GetOpts = {}): Promise { + async get(user, id) { + const { logger } = reportingCore.getPluginSetupDeps(); + if (!id) { + logger.warning(`No ID provided for GET`); + return; + } + + const username = getUsername(user); + + const body = getSearchBody({ + query: { + constant_score: { + filter: { + bool: { + must: [{ term: { _id: id } }, { term: { created_by: username } }], + }, + }, + }, + }, + size: 1, + }); + + const response = await execQuery((elasticsearchClient) => + elasticsearchClient.search({ body, index: getIndex() }) + ); + + const result = response?.body.hits.hits[0] as SearchHit | undefined; + if (!result || !result._source) { + logger.warning(`No hits resulted in search`); + return; + } + + const report = new Report({ ...result, ...result._source }); + return report.toApiJSON(); + }, + + async getContent(user, id) { if (!id) { return; } const username = getUsername(user); const body: SearchRequest['body'] = { - ...(opts.includeContent ? { _source: { excludes: [] } } : {}), + _source: { excludes: ['payload.headers'] }, query: { constant_score: { filter: { @@ -140,11 +195,17 @@ export function jobsQueryFactory(reportingCore: ReportingCore) { return; } - // FIXME: return the info in ReportApiJSON format; - return response.body.hits.hits[0] as ReportDocument; + const report = response.body.hits.hits[0] as ReportDocument; + + return { + status: report._source.status, + jobtype: report._source.jobtype, + output: report._source.output, + payload: report._source.payload, + }; }, - async delete(deleteIndex: string, id: string) { + async delete(deleteIndex, id) { try { const { asInternalUser: elasticsearchClient } = await reportingCore.getEsClient(); const query = { id, index: deleteIndex, refresh: true }; diff --git a/x-pack/plugins/rule_registry/server/index.ts b/x-pack/plugins/rule_registry/server/index.ts index b6fd6b9a605c0..19ea85b056bed 100644 --- a/x-pack/plugins/rule_registry/server/index.ts +++ b/x-pack/plugins/rule_registry/server/index.ts @@ -15,6 +15,11 @@ export { RuleDataClient } from './rule_data_client'; export { IRuleDataClient } from './rule_data_client/types'; export { getRuleExecutorData, RuleExecutorData } from './utils/get_rule_executor_data'; export { createLifecycleRuleTypeFactory } from './utils/create_lifecycle_rule_type_factory'; +export { + LifecycleRuleExecutor, + LifecycleAlertServices, + createLifecycleExecutor, +} from './utils/create_lifecycle_executor'; export { createPersistenceRuleTypeFactory } from './utils/create_persistence_rule_type_factory'; export const plugin = (initContext: PluginInitializerContext) => diff --git a/x-pack/plugins/rule_registry/server/types.ts b/x-pack/plugins/rule_registry/server/types.ts index f8bd1940b10a8..051789b1896bb 100644 --- a/x-pack/plugins/rule_registry/server/types.ts +++ b/x-pack/plugins/rule_registry/server/types.ts @@ -12,7 +12,7 @@ import { AlertTypeParams, AlertTypeState, } from '../../alerting/common'; -import { AlertType } from '../../alerting/server'; +import { AlertExecutorOptions, AlertServices, AlertType } from '../../alerting/server'; import { AlertsClient } from './alert_data_client/alerts_client'; type SimpleAlertType< @@ -41,6 +41,20 @@ export type AlertTypeWithExecutor< executor: AlertTypeExecutor; }; +export type AlertExecutorOptionsWithExtraServices< + Params extends AlertTypeParams = never, + State extends AlertTypeState = never, + InstanceState extends AlertInstanceState = never, + InstanceContext extends AlertInstanceContext = never, + ActionGroupIds extends string = never, + TExtraServices extends {} = never +> = Omit< + AlertExecutorOptions, + 'services' +> & { + services: AlertServices & TExtraServices; +}; + /** * @public */ diff --git a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts new file mode 100644 index 0000000000000..06c2cc8ff005d --- /dev/null +++ b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts @@ -0,0 +1,330 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Logger } from '@kbn/logging'; +import { getOrElse } from 'fp-ts/lib/Either'; +import * as rt from 'io-ts'; +import { Mutable } from 'utility-types'; +import { v4 } from 'uuid'; +import { + AlertExecutorOptions, + AlertInstance, + AlertInstanceContext, + AlertInstanceState, + AlertTypeParams, + AlertTypeState, +} from '../../../alerting/server'; +import { ParsedTechnicalFields, parseTechnicalFields } from '../../common/parse_technical_fields'; +import { + ALERT_DURATION, + ALERT_END, + ALERT_ID, + ALERT_START, + ALERT_STATUS, + ALERT_UUID, + EVENT_ACTION, + EVENT_KIND, + OWNER, + RULE_UUID, + TIMESTAMP, +} from '../../common/technical_rule_data_field_names'; +import { RuleDataClient } from '../rule_data_client'; +import { AlertExecutorOptionsWithExtraServices } from '../types'; +import { getRuleData } from './get_rule_executor_data'; + +type LifecycleAlertService< + InstanceState extends AlertInstanceState = never, + InstanceContext extends AlertInstanceContext = never, + ActionGroupIds extends string = never +> = (alert: { + id: string; + fields: Record; +}) => AlertInstance; + +export interface LifecycleAlertServices< + InstanceState extends AlertInstanceState = never, + InstanceContext extends AlertInstanceContext = never, + ActionGroupIds extends string = never +> { + alertWithLifecycle: LifecycleAlertService; +} + +export type LifecycleRuleExecutor< + Params extends AlertTypeParams = never, + State extends AlertTypeState = never, + InstanceState extends AlertInstanceState = never, + InstanceContext extends AlertInstanceContext = never, + ActionGroupIds extends string = never +> = ( + options: AlertExecutorOptionsWithExtraServices< + Params, + State, + InstanceState, + InstanceContext, + ActionGroupIds, + LifecycleAlertServices + > +) => Promise; + +const trackedAlertStateRt = rt.type({ + alertId: rt.string, + alertUuid: rt.string, + started: rt.string, +}); + +export type TrackedLifecycleAlertState = rt.TypeOf; + +const alertTypeStateRt = () => + rt.record(rt.string, rt.unknown) as rt.Type; + +const wrappedStateRt = () => + rt.type({ + wrapped: alertTypeStateRt(), + trackedAlerts: rt.record(rt.string, trackedAlertStateRt), + }); + +/** + * This is redefined instead of derived from above `wrappedStateRt` because + * there's no easy way to instantiate generic values such as the runtime type + * factory function. + */ +export type WrappedLifecycleRuleState = AlertTypeState & { + wrapped: State | void; + trackedAlerts: Record; +}; + +export const createLifecycleExecutor = (logger: Logger, ruleDataClient: RuleDataClient) => < + Params extends AlertTypeParams = never, + State extends AlertTypeState = never, + InstanceState extends AlertInstanceState = never, + InstanceContext extends AlertInstanceContext = never, + ActionGroupIds extends string = never +>( + wrappedExecutor: LifecycleRuleExecutor< + Params, + State, + InstanceState, + InstanceContext, + ActionGroupIds + > +) => async ( + options: AlertExecutorOptions< + Params, + WrappedLifecycleRuleState, + InstanceState, + InstanceContext, + ActionGroupIds + > +): Promise> => { + const { + rule, + services: { alertInstanceFactory }, + state: previousState, + } = options; + + const ruleExecutorData = getRuleData(options); + + const state = getOrElse( + (): WrappedLifecycleRuleState => ({ + wrapped: previousState as State, + trackedAlerts: {}, + }) + )(wrappedStateRt().decode(previousState)); + + const currentAlerts: Record = {}; + + const timestamp = options.startedAt.toISOString(); + + const lifecycleAlertServices: LifecycleAlertServices< + InstanceState, + InstanceContext, + ActionGroupIds + > = { + alertWithLifecycle: ({ id, fields }) => { + currentAlerts[id] = { + ...fields, + [ALERT_ID]: id, + }; + return alertInstanceFactory(id); + }, + }; + + const nextWrappedState = await wrappedExecutor({ + ...options, + state: state.wrapped != null ? state.wrapped : ({} as State), + services: { + ...options.services, + ...lifecycleAlertServices, + }, + }); + + const currentAlertIds = Object.keys(currentAlerts); + const trackedAlertIds = Object.keys(state.trackedAlerts); + const newAlertIds = currentAlertIds.filter((alertId) => !trackedAlertIds.includes(alertId)); + + const allAlertIds = [...new Set(currentAlertIds.concat(trackedAlertIds))]; + + const trackedAlertStatesOfRecovered = Object.values(state.trackedAlerts).filter( + (trackedAlertState) => !currentAlerts[trackedAlertState.alertId] + ); + + logger.debug( + `Tracking ${allAlertIds.length} alerts (${newAlertIds.length} new, ${trackedAlertStatesOfRecovered.length} recovered)` + ); + + const alertsDataMap: Record< + string, + { + [ALERT_ID]: string; + } + > = { + ...currentAlerts, + }; + + if (trackedAlertStatesOfRecovered.length) { + const { hits } = await ruleDataClient.getReader().search({ + body: { + query: { + bool: { + filter: [ + { + term: { + [RULE_UUID]: ruleExecutorData[RULE_UUID], + }, + }, + { + terms: { + [ALERT_UUID]: trackedAlertStatesOfRecovered.map( + (trackedAlertState) => trackedAlertState.alertUuid + ), + }, + }, + ], + }, + }, + size: trackedAlertStatesOfRecovered.length, + collapse: { + field: ALERT_UUID, + }, + _source: false, + fields: [{ field: '*', include_unmapped: true }], + sort: { + [TIMESTAMP]: 'desc' as const, + }, + }, + allow_no_indices: true, + }); + + hits.hits.forEach((hit) => { + const fields = parseTechnicalFields(hit.fields); + const alertId = fields[ALERT_ID]!; + alertsDataMap[alertId] = { + ...fields, + [ALERT_ID]: alertId, + }; + }); + } + + const eventsToIndex = allAlertIds.map((alertId) => { + const alertData = alertsDataMap[alertId]; + + if (!alertData) { + logger.warn(`Could not find alert data for ${alertId}`); + } + + const event: Mutable = { + ...alertData, + ...ruleExecutorData, + [TIMESTAMP]: timestamp, + [EVENT_KIND]: 'event', + [OWNER]: rule.consumer, + [ALERT_ID]: alertId, + }; + + const isNew = !state.trackedAlerts[alertId]; + const isRecovered = !currentAlerts[alertId]; + const isActiveButNotNew = !isNew && !isRecovered; + const isActive = !isRecovered; + + const { alertUuid, started } = state.trackedAlerts[alertId] ?? { + alertUuid: v4(), + started: timestamp, + }; + + event[ALERT_START] = started; + event[ALERT_UUID] = alertUuid; + + if (isNew) { + event[EVENT_ACTION] = 'open'; + } + + if (isRecovered) { + event[ALERT_END] = timestamp; + event[EVENT_ACTION] = 'close'; + event[ALERT_STATUS] = 'closed'; + } + + if (isActiveButNotNew) { + event[EVENT_ACTION] = 'active'; + } + + if (isActive) { + event[ALERT_STATUS] = 'open'; + } + + event[ALERT_DURATION] = + (options.startedAt.getTime() - new Date(event[ALERT_START]!).getTime()) * 1000; + + return event; + }); + + if (eventsToIndex.length) { + const alertEvents: Map = new Map(); + + for (const event of eventsToIndex) { + const uuid = event[ALERT_UUID]!; + let storedEvent = alertEvents.get(uuid); + if (!storedEvent) { + storedEvent = event; + } + alertEvents.set(uuid, { + ...storedEvent, + [EVENT_KIND]: 'signal', + }); + } + logger.debug(`Preparing to index ${eventsToIndex.length} alerts.`); + + if (ruleDataClient.isWriteEnabled()) { + await ruleDataClient.getWriter().bulk({ + body: eventsToIndex + .flatMap((event) => [{ index: {} }, event]) + .concat( + Array.from(alertEvents.values()).flatMap((event) => [ + { index: { _id: event[ALERT_UUID]! } }, + event, + ]) + ), + }); + } + } + + const nextTrackedAlerts = Object.fromEntries( + eventsToIndex + .filter((event) => event[ALERT_STATUS] !== 'closed') + .map((event) => { + const alertId = event[ALERT_ID]!; + const alertUuid = event[ALERT_UUID]!; + const started = new Date(event[ALERT_START]!).toISOString(); + return [alertId, { alertId, alertUuid, started }]; + }) + ); + + return { + wrapped: nextWrappedState ?? ({} as State), + trackedAlerts: ruleDataClient.isWriteEnabled() ? nextTrackedAlerts : {}, + }; +}; diff --git a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type.test.ts b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type.test.ts index a37ba9ef56636..3e7fbbe5cbc59 100644 --- a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type.test.ts +++ b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type.test.ts @@ -38,11 +38,11 @@ function createRule() { }); nextAlerts = []; }, - id: 'test_type', + id: 'ruleTypeId', minimumLicenseRequired: 'basic', isExportable: true, - name: 'Test type', - producer: 'test', + name: 'ruleTypeName', + producer: 'producer', actionVariables: { context: [], params: [], @@ -195,11 +195,11 @@ describe('createLifecycleRuleTypeFactory', () => { "kibana.rac.alert.duration.us": 0, "kibana.rac.alert.id": "opbeans-java", "kibana.rac.alert.owner": "consumer", - "kibana.rac.alert.producer": "test", + "kibana.rac.alert.producer": "producer", "kibana.rac.alert.start": "2021-06-16T09:01:00.000Z", "kibana.rac.alert.status": "open", - "rule.category": "Test type", - "rule.id": "test_type", + "rule.category": "ruleTypeName", + "rule.id": "ruleTypeId", "rule.name": "name", "rule.uuid": "alertId", "service.name": "opbeans-java", @@ -214,11 +214,11 @@ describe('createLifecycleRuleTypeFactory', () => { "kibana.rac.alert.duration.us": 0, "kibana.rac.alert.id": "opbeans-node", "kibana.rac.alert.owner": "consumer", - "kibana.rac.alert.producer": "test", + "kibana.rac.alert.producer": "producer", "kibana.rac.alert.start": "2021-06-16T09:01:00.000Z", "kibana.rac.alert.status": "open", - "rule.category": "Test type", - "rule.id": "test_type", + "rule.category": "ruleTypeName", + "rule.id": "ruleTypeId", "rule.name": "name", "rule.uuid": "alertId", "service.name": "opbeans-node", @@ -233,11 +233,11 @@ describe('createLifecycleRuleTypeFactory', () => { "kibana.rac.alert.duration.us": 0, "kibana.rac.alert.id": "opbeans-java", "kibana.rac.alert.owner": "consumer", - "kibana.rac.alert.producer": "test", + "kibana.rac.alert.producer": "producer", "kibana.rac.alert.start": "2021-06-16T09:01:00.000Z", "kibana.rac.alert.status": "open", - "rule.category": "Test type", - "rule.id": "test_type", + "rule.category": "ruleTypeName", + "rule.id": "ruleTypeId", "rule.name": "name", "rule.uuid": "alertId", "service.name": "opbeans-java", @@ -252,11 +252,11 @@ describe('createLifecycleRuleTypeFactory', () => { "kibana.rac.alert.duration.us": 0, "kibana.rac.alert.id": "opbeans-node", "kibana.rac.alert.owner": "consumer", - "kibana.rac.alert.producer": "test", + "kibana.rac.alert.producer": "producer", "kibana.rac.alert.start": "2021-06-16T09:01:00.000Z", "kibana.rac.alert.status": "open", - "rule.category": "Test type", - "rule.id": "test_type", + "rule.category": "ruleTypeName", + "rule.id": "ruleTypeId", "rule.name": "name", "rule.uuid": "alertId", "service.name": "opbeans-node", diff --git a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type_factory.ts b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type_factory.ts index 34045a2a905f8..cf1be1bd32013 100644 --- a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type_factory.ts +++ b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type_factory.ts @@ -5,51 +5,26 @@ * 2.0. */ import { Logger } from '@kbn/logging'; -import { isLeft } from 'fp-ts/lib/Either'; -import * as t from 'io-ts'; -import { Mutable } from 'utility-types'; -import v4 from 'uuid/v4'; -import { AlertInstance } from '../../../alerting/server'; import { RuleDataClient } from '..'; import { AlertInstanceContext, AlertInstanceState, AlertTypeParams, + AlertTypeState, } from '../../../alerting/common'; -import { - ALERT_DURATION, - ALERT_END, - ALERT_ID, - ALERT_START, - ALERT_STATUS, - ALERT_UUID, - EVENT_ACTION, - EVENT_KIND, - OWNER, - RULE_UUID, - TIMESTAMP, -} from '../../common/technical_rule_data_field_names'; +import { AlertInstance } from '../../../alerting/server'; import { AlertTypeWithExecutor } from '../types'; -import { ParsedTechnicalFields, parseTechnicalFields } from '../../common/parse_technical_fields'; -import { getRuleExecutorData } from './get_rule_executor_data'; +import { createLifecycleExecutor } from './create_lifecycle_executor'; export type LifecycleAlertService> = (alert: { id: string; fields: Record; }) => AlertInstance; -const trackedAlertStateRt = t.type({ - alertId: t.string, - alertUuid: t.string, - started: t.string, -}); - -const wrappedStateRt = t.type({ - wrapped: t.record(t.string, t.unknown), - trackedAlerts: t.record(t.string, trackedAlertStateRt), -}); - -type CreateLifecycleRuleTypeFactory = (options: { +export const createLifecycleRuleTypeFactory = ({ + logger, + ruleDataClient, +}: { ruleDataClient: RuleDataClient; logger: Logger; }) => < @@ -58,216 +33,17 @@ type CreateLifecycleRuleTypeFactory = (options: { TServices extends { alertWithLifecycle: LifecycleAlertService } >( type: AlertTypeWithExecutor -) => AlertTypeWithExecutor; - -export const createLifecycleRuleTypeFactory: CreateLifecycleRuleTypeFactory = ({ - logger, - ruleDataClient, -}) => (type) => { +): AlertTypeWithExecutor => { + const createBoundLifecycleExecutor = createLifecycleExecutor(logger, ruleDataClient); + const executor = createBoundLifecycleExecutor< + TParams, + AlertTypeState, + AlertInstanceState, + TAlertInstanceContext, + string + >(type.executor as any); return { ...type, - executor: async (options) => { - const { - services: { alertInstanceFactory }, - state: previousState, - rule, - } = options; - - const ruleExecutorData = getRuleExecutorData(type, options); - - const decodedState = wrappedStateRt.decode(previousState); - - const state = isLeft(decodedState) - ? { - wrapped: previousState, - trackedAlerts: {}, - } - : decodedState.right; - - const currentAlerts: Record = {}; - - const timestamp = options.startedAt.toISOString(); - - const nextWrappedState = await type.executor({ - ...options, - state: state.wrapped, - services: { - ...options.services, - alertWithLifecycle: ({ id, fields }) => { - currentAlerts[id] = { - ...fields, - [ALERT_ID]: id, - }; - return alertInstanceFactory(id); - }, - }, - }); - - const currentAlertIds = Object.keys(currentAlerts); - const trackedAlertIds = Object.keys(state.trackedAlerts); - const newAlertIds = currentAlertIds.filter((alertId) => !trackedAlertIds.includes(alertId)); - - const allAlertIds = [...new Set(currentAlertIds.concat(trackedAlertIds))]; - - const trackedAlertStatesOfRecovered = Object.values(state.trackedAlerts).filter( - (trackedAlertState) => !currentAlerts[trackedAlertState.alertId] - ); - - logger.debug( - `Tracking ${allAlertIds.length} alerts (${newAlertIds.length} new, ${trackedAlertStatesOfRecovered.length} recovered)` - ); - - const alertsDataMap: Record< - string, - { - [ALERT_ID]: string; - } - > = { - ...currentAlerts, - }; - - if (trackedAlertStatesOfRecovered.length) { - const { hits } = await ruleDataClient.getReader().search({ - body: { - query: { - bool: { - filter: [ - { - term: { - [RULE_UUID]: ruleExecutorData[RULE_UUID], - }, - }, - { - terms: { - [ALERT_UUID]: trackedAlertStatesOfRecovered.map( - (trackedAlertState) => trackedAlertState.alertUuid - ), - }, - }, - ], - }, - }, - size: trackedAlertStatesOfRecovered.length, - collapse: { - field: ALERT_UUID, - }, - _source: false, - fields: [{ field: '*', include_unmapped: true }], - sort: { - [TIMESTAMP]: 'desc' as const, - }, - }, - allow_no_indices: true, - }); - - hits.hits.forEach((hit) => { - const fields = parseTechnicalFields(hit.fields); - const alertId = fields[ALERT_ID]!; - alertsDataMap[alertId] = { - ...fields, - [ALERT_ID]: alertId, - }; - }); - } - - const eventsToIndex = allAlertIds.map((alertId) => { - const alertData = alertsDataMap[alertId]; - - if (!alertData) { - logger.warn(`Could not find alert data for ${alertId}`); - } - - const event: Mutable = { - ...alertData, - ...ruleExecutorData, - [TIMESTAMP]: timestamp, - [EVENT_KIND]: 'event', - [OWNER]: rule.consumer, - [ALERT_ID]: alertId, - }; - - const isNew = !state.trackedAlerts[alertId]; - const isRecovered = !currentAlerts[alertId]; - const isActiveButNotNew = !isNew && !isRecovered; - const isActive = !isRecovered; - - const { alertUuid, started } = state.trackedAlerts[alertId] ?? { - alertUuid: v4(), - started: timestamp, - }; - - event[ALERT_START] = started; - event[ALERT_UUID] = alertUuid; - - if (isNew) { - event[EVENT_ACTION] = 'open'; - } - - if (isRecovered) { - event[ALERT_END] = timestamp; - event[EVENT_ACTION] = 'close'; - event[ALERT_STATUS] = 'closed'; - } - - if (isActiveButNotNew) { - event[EVENT_ACTION] = 'active'; - } - - if (isActive) { - event[ALERT_STATUS] = 'open'; - } - - event[ALERT_DURATION] = - (options.startedAt.getTime() - new Date(event[ALERT_START]!).getTime()) * 1000; - - return event; - }); - - if (eventsToIndex.length) { - const alertEvents: Map = new Map(); - - for (const event of eventsToIndex) { - const uuid = event[ALERT_UUID]!; - let storedEvent = alertEvents.get(uuid); - if (!storedEvent) { - storedEvent = event; - } - alertEvents.set(uuid, { - ...storedEvent, - [EVENT_KIND]: 'signal', - }); - } - logger.debug(`Preparing to index ${eventsToIndex.length} alerts.`); - - if (ruleDataClient.isWriteEnabled()) { - await ruleDataClient.getWriter().bulk({ - body: eventsToIndex - .flatMap((event) => [{ index: {} }, event]) - .concat( - Array.from(alertEvents.values()).flatMap((event) => [ - { index: { _id: event[ALERT_UUID]! } }, - event, - ]) - ), - }); - } - } - - const nextTrackedAlerts = Object.fromEntries( - eventsToIndex - .filter((event) => event[ALERT_STATUS] !== 'closed') - .map((event) => { - const alertId = event[ALERT_ID]!; - const alertUuid = event[ALERT_UUID]!; - const started = new Date(event[ALERT_START]!).toISOString(); - return [alertId, { alertId, alertUuid, started }]; - }) - ); - - return { - wrapped: nextWrappedState ?? {}, - trackedAlerts: ruleDataClient.isWriteEnabled() ? nextTrackedAlerts : {}, - }; - }, + executor: executor as any, }; }; diff --git a/x-pack/plugins/rule_registry/server/utils/get_rule_executor_data.ts b/x-pack/plugins/rule_registry/server/utils/get_rule_executor_data.ts index 1ea640add7b48..7cb02428322a6 100644 --- a/x-pack/plugins/rule_registry/server/utils/get_rule_executor_data.ts +++ b/x-pack/plugins/rule_registry/server/utils/get_rule_executor_data.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { AlertExecutorOptions } from '../../../alerting/server'; import { PRODUCER, RULE_CATEGORY, @@ -37,3 +38,14 @@ export function getRuleExecutorData( [PRODUCER]: type.producer, }; } + +export function getRuleData(options: AlertExecutorOptions) { + return { + [RULE_ID]: options.rule.ruleTypeId, + [RULE_UUID]: options.alertId, + [RULE_CATEGORY]: options.rule.ruleTypeName, + [RULE_NAME]: options.rule.name, + [TAGS]: options.tags, + [PRODUCER]: options.rule.producer, + }; +} diff --git a/x-pack/plugins/rule_registry/server/utils/rbac.ts b/x-pack/plugins/rule_registry/server/utils/rbac.ts index 812dbb8408812..e07c4394be2f1 100644 --- a/x-pack/plugins/rule_registry/server/utils/rbac.ts +++ b/x-pack/plugins/rule_registry/server/utils/rbac.ts @@ -9,9 +9,14 @@ * registering a new instance of the rule data client * in a new plugin will require updating the below data structure * to include the index name where the alerts as data will be written to. + * + * This doesn't work in combination with the `xpack.ruleRegistry.index` + * setting, with which the user can change the index prefix. */ export const mapConsumerToIndexName = { apm: '.alerts-observability-apm', + logs: '.alerts-observability.logs', + infrastructure: '.alerts-observability.metrics', observability: '.alerts-observability', siem: ['.alerts-security.alerts', '.siem-signals'], }; diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index abfcb4014a79f..27d4a5c9fd399 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -110,6 +110,7 @@ export const APP_EVENT_FILTERS_PATH = `${APP_PATH}${EVENT_FILTERS_PATH}`; /** The comma-delimited list of Elasticsearch indices from which the SIEM app collects events */ export const DEFAULT_INDEX_PATTERN = [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', diff --git a/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.test.ts b/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.test.ts index 7de082e778a07..b38886296e74d 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.test.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.test.ts @@ -1150,7 +1150,7 @@ describe('get_filter', () => { }, { field: '@timestamp', - format: 'epoch_millis', + format: 'strict_date_optional_time', }, ], }, @@ -1195,9 +1195,13 @@ describe('get_filter', () => { field: '*', include_unmapped: true, }, + { + field: 'event.ingested', + format: 'strict_date_optional_time', + }, { field: '@timestamp', - format: 'epoch_millis', + format: 'strict_date_optional_time', }, ], }, @@ -1289,7 +1293,7 @@ describe('get_filter', () => { }, { field: '@timestamp', - format: 'epoch_millis', + format: 'strict_date_optional_time', }, ], }, diff --git a/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.ts b/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.ts index 86e66577abd45..1e7bcb0002dad 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.ts @@ -79,6 +79,15 @@ export const buildEqlSearchRequest = ( eventCategoryOverride: string | undefined ): EqlSearchRequest => { const timestamp = timestampOverride ?? '@timestamp'; + + const defaultTimeFields = ['@timestamp']; + const timestamps = + timestampOverride != null ? [timestampOverride, ...defaultTimeFields] : defaultTimeFields; + const docFields = timestamps.map((tstamp) => ({ + field: tstamp, + format: 'strict_date_optional_time', + })); + // Assume that `indices.query.bool.max_clause_count` is at least 1024 (the default value), // allowing us to make 1024-item chunks of exception list items. // Discussion at https://issues.apache.org/jira/browse/LUCENE-4835 indicates that 1024 is a @@ -126,14 +135,7 @@ export const buildEqlSearchRequest = ( field: '*', include_unmapped: true, }, - { - field: '@timestamp', - // BUG: We have to format @timestamp until this bug is fixed with epoch_millis - // https://github.com/elastic/elasticsearch/issues/74582 - // TODO: Remove epoch and use the same techniques from x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.ts - // where we format both the timestamp and any overrides as ISO8601 - format: 'epoch_millis', - }, + ...docFields, ], }, }; diff --git a/x-pack/plugins/security_solution/common/ecs/file/index.ts b/x-pack/plugins/security_solution/common/ecs/file/index.ts index 5e409b1095cf5..0c9dde20011f4 100644 --- a/x-pack/plugins/security_solution/common/ecs/file/index.ts +++ b/x-pack/plugins/security_solution/common/ecs/file/index.ts @@ -14,9 +14,42 @@ export interface CodeSignature { subject_name: string[]; trusted: string[]; } + +export interface Token { + integrity_level_name: string; +} + +export interface MemoryPe { + imphash?: string; +} + +export interface StartAddressDetails { + allocation_base?: number; + allocation_protection?: string; + allocation_size?: number; + allocation_type?: string; + bytes_address?: number; + bytes_allocation_offset?: number; + bytes_compressed?: string; + bytes_compressed_present?: string; + mapped_path?: string; + mapped_pe_detected?: boolean; + memory_pe_detected?: boolean; + region_base?: number; + region_protection?: string; + region_size?: number; + region_state?: string; + strings?: string; + memory_pe?: MemoryPe; +} + export interface Ext { code_signature?: CodeSignature[] | CodeSignature; original?: Original; + token?: Token; + start_address_allocation_offset?: number; + start_address_bytes_disasm_hash?: string; + start_address_details?: StartAddressDetails; } export interface Hash { md5?: string[]; diff --git a/x-pack/plugins/security_solution/common/ecs/index.ts b/x-pack/plugins/security_solution/common/ecs/index.ts index 8054b3c8521db..610a2fd1f6e9e 100644 --- a/x-pack/plugins/security_solution/common/ecs/index.ts +++ b/x-pack/plugins/security_solution/common/ecs/index.ts @@ -30,6 +30,8 @@ import { ProcessEcs } from './process'; import { SystemEcs } from './system'; import { ThreatEcs } from './threat'; import { Ransomware } from './ransomware'; +import { MemoryProtection } from './memory_protection'; +import { Target } from './target_type'; export interface Ecs { _id: string; @@ -63,4 +65,7 @@ export interface Ecs { // This should be temporary eql?: { parentId: string; sequenceNumber: string }; Ransomware?: Ransomware; + // eslint-disable-next-line @typescript-eslint/naming-convention + Memory_protection?: MemoryProtection; + Target?: Target; } diff --git a/x-pack/plugins/security_solution/common/ecs/memory_protection/index.ts b/x-pack/plugins/security_solution/common/ecs/memory_protection/index.ts new file mode 100644 index 0000000000000..8115fc0dcd26e --- /dev/null +++ b/x-pack/plugins/security_solution/common/ecs/memory_protection/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +export interface MemoryProtection { + cross_session?: boolean; + feature?: string; + parent_to_child?: boolean; + self_injection?: boolean; + unique_key_v1?: string; +} diff --git a/x-pack/plugins/security_solution/common/ecs/process/index.ts b/x-pack/plugins/security_solution/common/ecs/process/index.ts index 820ecc5560e6c..0eb2400466e64 100644 --- a/x-pack/plugins/security_solution/common/ecs/process/index.ts +++ b/x-pack/plugins/security_solution/common/ecs/process/index.ts @@ -37,4 +37,5 @@ export interface ProcessParentData { export interface Thread { id?: number[]; start?: string[]; + Ext?: Ext; } diff --git a/x-pack/plugins/console_extensions/server/config.ts b/x-pack/plugins/security_solution/common/ecs/target_type/index.ts similarity index 56% rename from x-pack/plugins/console_extensions/server/config.ts rename to x-pack/plugins/security_solution/common/ecs/target_type/index.ts index 15b06bf93ffbe..3c19b51173a04 100644 --- a/x-pack/plugins/console_extensions/server/config.ts +++ b/x-pack/plugins/security_solution/common/ecs/target_type/index.ts @@ -5,10 +5,8 @@ * 2.0. */ -import { schema, TypeOf } from '@kbn/config-schema'; +import { ProcessEcs } from '../process'; -export type ConfigType = TypeOf; - -export const config = schema.object({ - enabled: schema.boolean({ defaultValue: true }), -}); +export interface Target { + process: ProcessEcs; +} 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 876cb3866c614..255ab8f0a598c 100644 --- a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts @@ -7,6 +7,7 @@ import uuid from 'uuid'; import seedrandom from 'seedrandom'; +import { assertNever } from '@kbn/std'; import { AlertEvent, DataStream, @@ -387,6 +388,12 @@ const eventsDefaultDataStream = { namespace: 'default', }; +enum AlertTypes { + MALWARE = 'MALWARE', + MEMORY_SIGNATURE = 'MEMORY_SIGNATURE', + MEMORY_SHELLCODE = 'MEMORY_SHELLCODE', +} + const alertsDefaultDataStream = { type: 'logs', dataset: 'endpoint.alerts', @@ -509,16 +516,15 @@ export class EndpointDocGenerator extends BaseDataGenerator { data_stream: metadataDataStream, }; } - /** - * Creates an alert from the simulated host represented by this EndpointDocGenerator + * Creates a malware alert from the simulated host represented by this EndpointDocGenerator * @param ts - Timestamp to put in the event * @param entityID - entityID of the originating process * @param parentEntityID - optional entityID of the parent process, if it exists * @param ancestry - an array of ancestors for the generated alert * @param alertsDataStream the values to populate the data_stream fields when generating alert documents */ - public generateAlert({ + public generateMalwareAlert({ ts = new Date().getTime(), entityID = this.randomString(10), parentEntityID, @@ -619,37 +625,198 @@ export class EndpointDocGenerator extends BaseDataGenerator { }, }, }, - dll: [ - { - pe: { - architecture: 'x64', + dll: this.getAlertsDefaultDll(), + }; + } + + /** + * Creates a memory alert from the simulated host represented by this EndpointDocGenerator + * @param ts - Timestamp to put in the event + * @param entityID - entityID of the originating process + * @param parentEntityID - optional entityID of the parent process, if it exists + * @param ancestry - an array of ancestors for the generated alert + * @param alertsDataStream the values to populate the data_stream fields when generating alert documents + */ + public generateMemoryAlert({ + ts = new Date().getTime(), + entityID = this.randomString(10), + parentEntityID, + ancestry = [], + alertsDataStream = alertsDefaultDataStream, + alertType, + }: { + ts?: number; + entityID?: string; + parentEntityID?: string; + ancestry?: string[]; + alertsDataStream?: DataStream; + alertType?: AlertTypes; + } = {}): AlertEvent { + const processName = this.randomProcessName(); + const isShellcode = alertType === AlertTypes.MEMORY_SHELLCODE; + const newAlert: AlertEvent = { + ...this.commonInfo, + data_stream: alertsDataStream, + '@timestamp': ts, + ecs: { + version: '1.6.0', + }, + // disabling naming-convention to accommodate external field + // eslint-disable-next-line @typescript-eslint/naming-convention + Memory_protection: { + feature: isShellcode ? 'shellcode_thread' : 'signature', + self_injection: true, + }, + event: { + action: 'start', + kind: 'alert', + category: 'malware', + code: isShellcode ? 'malicious_thread' : 'memory_signature', + id: this.seededUUIDv4(), + dataset: 'endpoint', + module: 'endpoint', + type: 'info', + sequence: this.sequence++, + }, + file: {}, + process: { + pid: 2, + name: processName, + start: ts, + uptime: 0, + entity_id: entityID, + executable: `C:/fake/${processName}`, + parent: parentEntityID ? { entity_id: parentEntityID, pid: 1 } : undefined, + hash: { + md5: 'fake md5', + sha1: 'fake sha1', + sha256: 'fake sha256', + }, + Ext: { + ancestry, + code_signature: [ + { + trusted: false, + subject_name: 'bad signer', + }, + ], + user: 'SYSTEM', + token: { + integrity_level_name: 'high', }, - code_signature: { - subject_name: 'Cybereason Inc', - trusted: true, + malware_signature: { + all_names: 'Windows.Trojan.FakeAgent', + identifier: 'diagnostic-malware-signature-v1-fake', }, + }, + }, + dll: this.getAlertsDefaultDll(), + }; - hash: { - md5: '1f2d082566b0fc5f2c238a5180db7451', - sha1: 'ca85243c0af6a6471bdaa560685c51eefd6dbc0d', - sha256: '8ad40c90a611d36eb8f9eb24fa04f7dbca713db383ff55a03aa0f382e92061a2', + // shellcode_thread memory alert have an additional process field + if (isShellcode) { + newAlert.Target = { + process: { + thread: { + Ext: { + start_address_allocation_offset: 0, + start_address_bytes_disasm_hash: 'a disam hash', + start_address_details: { + allocation_type: 'PRIVATE', + allocation_size: 4000, + region_size: 4000, + region_protection: 'RWX', + memory_pe: { + imphash: 'a hash', + }, + }, + }, }, + }, + }; + } + return newAlert; + } + /** + * Creates an alert from the simulated host represented by this EndpointDocGenerator + * @param ts - Timestamp to put in the event + * @param entityID - entityID of the originating process + * @param parentEntityID - optional entityID of the parent process, if it exists + * @param ancestry - an array of ancestors for the generated alert + * @param alertsDataStream the values to populate the data_stream fields when generating alert documents + */ + public generateAlert({ + ts = new Date().getTime(), + entityID = this.randomString(10), + parentEntityID, + ancestry = [], + alertsDataStream = alertsDefaultDataStream, + }: { + ts?: number; + entityID?: string; + parentEntityID?: string; + ancestry?: string[]; + alertsDataStream?: DataStream; + } = {}): AlertEvent { + const alertType = this.randomChoice(Object.values(AlertTypes)); + switch (alertType) { + case AlertTypes.MALWARE: + return this.generateMalwareAlert({ + ts, + entityID, + parentEntityID, + ancestry, + alertsDataStream, + }); + case AlertTypes.MEMORY_SIGNATURE: + case AlertTypes.MEMORY_SHELLCODE: + return this.generateMemoryAlert({ + ts, + entityID, + parentEntityID, + ancestry, + alertsDataStream, + alertType, + }); + default: + return assertNever(alertType); + } + } - path: 'C:\\Program Files\\Cybereason ActiveProbe\\AmSvc.exe', - Ext: { - compile_time: 1534424710, - mapped_address: 5362483200, - mapped_size: 0, - malware_classification: { - identifier: 'Whitelisted', - score: 0, - threshold: 0, - version: '3.0.0', - }, + /** + * Returns the default DLLs used in alerts + */ + private getAlertsDefaultDll() { + return [ + { + pe: { + architecture: 'x64', + }, + code_signature: { + subject_name: 'Cybereason Inc', + trusted: true, + }, + + hash: { + md5: '1f2d082566b0fc5f2c238a5180db7451', + sha1: 'ca85243c0af6a6471bdaa560685c51eefd6dbc0d', + sha256: '8ad40c90a611d36eb8f9eb24fa04f7dbca713db383ff55a03aa0f382e92061a2', + }, + + path: 'C:\\Program Files\\Cybereason ActiveProbe\\AmSvc.exe', + Ext: { + compile_time: 1534424710, + mapped_address: 5362483200, + mapped_size: 0, + malware_classification: { + identifier: 'Whitelisted', + score: 0, + threshold: 0, + version: '3.0.0', }, }, - ], - }; + }, + ]; } /** 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 3cd4a422a5fe8..076eb51a5fdc5 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/index.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/index.ts @@ -295,6 +295,31 @@ export type AlertEvent = Partial<{ }>; }>; }>; + // disabling naming-convention to accommodate external field + // eslint-disable-next-line @typescript-eslint/naming-convention + Memory_protection: Partial<{ + feature: ECSField; + self_injection: ECSField; + }>; + Target: Partial<{ + process: Partial<{ + thread: Partial<{ + Ext: Partial<{ + start_address_allocation_offset: ECSField; + start_address_bytes_disasm_hash: ECSField; + start_address_details: Partial<{ + allocation_type: ECSField; + allocation_size: ECSField; + region_size: ECSField; + region_protection: ECSField; + memory_pe: Partial<{ + imphash: ECSField; + }>; + }>; + }>; + }>; + }>; + }>; process: Partial<{ command_line: ECSField; ppid: ECSField; @@ -328,6 +353,10 @@ export type AlertEvent = Partial<{ >; }>; user: ECSField; + malware_signature: Partial<{ + all_names: ECSField; + identifier: ECSField; + }>; }>; }>; file: Partial<{ diff --git a/x-pack/plugins/security_solution/cypress/integration/timelines/row_renderers.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timelines/row_renderers.spec.ts index 9986d9d2afbd9..0755142fbdc58 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timelines/row_renderers.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timelines/row_renderers.spec.ts @@ -20,26 +20,6 @@ import { populateTimeline } from '../../tasks/timeline'; import { HOSTS_URL } from '../../urls/navigation'; -const RowRenderersId = [ - 'alerts', - 'auditd', - 'auditd_file', - 'library', - 'netflow', - 'plain', - 'registry', - 'suricata', - 'system', - 'system_dns', - 'system_endgame_process', - 'system_file', - 'system_fim', - 'system_security_event', - 'system_socket', - 'threat_match', - 'zeek', -]; - describe('Row renderers', () => { beforeEach(() => { cleanKibana(); @@ -100,9 +80,5 @@ describe('Row renderers', () => { .should('not.be.checked'); cy.wait('@updateTimeline').its('response.statusCode').should('eq', 200); - - cy.wait('@updateTimeline').then((interception) => { - expect(interception.request.body.timeline.excludedRowRendererIds).to.eql(RowRenderersId); - }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/objects/rule.ts b/x-pack/plugins/security_solution/cypress/objects/rule.ts index a10fa5b0eda78..7589c8fab3dae 100644 --- a/x-pack/plugins/security_solution/cypress/objects/rule.ts +++ b/x-pack/plugins/security_solution/cypress/objects/rule.ts @@ -98,6 +98,7 @@ export interface MachineLearningRule { export const getIndexPatterns = (): string[] => [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', diff --git a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts index 78298c9881077..a260f3ec48955 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts @@ -190,7 +190,8 @@ export const sortByActivatedRules = () => { export const waitForRulesTableToBeLoaded = () => { cy.get(RULES_TABLE_INITIAL_LOADING_INDICATOR).should('exist'); - cy.get(RULES_TABLE_INITIAL_LOADING_INDICATOR).should('not.exist'); + // Wait up to 5 minutes for the rules to load as in CI containers this can be very slow + cy.get(RULES_TABLE_INITIAL_LOADING_INDICATOR, { timeout: 300000 }).should('not.exist'); }; export const waitForRulesTableToBeRefreshed = () => { diff --git a/x-pack/plugins/security_solution/public/cases/components/__mock__/form.ts b/x-pack/plugins/security_solution/public/cases/components/__mock__/form.ts index 9ec356f70f9a4..3ba7aa616f1c1 100644 --- a/x-pack/plugins/security_solution/public/cases/components/__mock__/form.ts +++ b/x-pack/plugins/security_solution/public/cases/components/__mock__/form.ts @@ -25,6 +25,7 @@ export const mockFormHook = { setFieldErrors: jest.fn(), getFields: jest.fn(), getFormData: jest.fn(), + getFieldDefaultValue: jest.fn(), /* Returns a list of all errors in the form */ getErrors: jest.fn(), reset: jest.fn(), @@ -35,7 +36,6 @@ export const mockFormHook = { __validateFields: jest.fn(), __updateFormDataAt: jest.fn(), __readFieldConfigFromSchema: jest.fn(), - __getFieldDefaultValue: jest.fn(), }; // eslint-disable-next-line @typescript-eslint/no-explicit-any export const getFormMock = (sampleData: any) => ({ diff --git a/x-pack/plugins/security_solution/public/common/components/autocomplete/field.tsx b/x-pack/plugins/security_solution/public/common/components/autocomplete/field.tsx index d4185fe639695..a175a9b847c71 100644 --- a/x-pack/plugins/security_solution/public/common/components/autocomplete/field.tsx +++ b/x-pack/plugins/security_solution/public/common/components/autocomplete/field.tsx @@ -25,6 +25,13 @@ interface OperatorProps { onChange: (a: IFieldType[]) => void; } +/** + * There is a copy within: + * x-pack/plugins/lists/public/exceptions/components/autocomplete/field.tsx + * + * TODO: This should be in its own packaged and not copied, https://github.com/elastic/kibana/issues/105378 + * NOTE: This has deviated from the copy and will have to be reconciled. + */ export const FieldComponent: React.FC = ({ placeholder, selectedField, diff --git a/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_exists.test.tsx b/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_exists.test.tsx deleted file mode 100644 index b6300581f12dd..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_exists.test.tsx +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { mount } from 'enzyme'; - -import { AutocompleteFieldExistsComponent } from './field_value_exists'; - -describe('AutocompleteFieldExistsComponent', () => { - test('it renders field disabled', () => { - const wrapper = mount(); - - expect( - wrapper - .find(`[data-test-subj="valuesAutocompleteComboBox existsComboxBox"] input`) - .prop('disabled') - ).toBeTruthy(); - }); -}); diff --git a/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_exists.tsx b/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_exists.tsx deleted file mode 100644 index 715ba52701177..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_exists.tsx +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { EuiFormRow, EuiComboBox } from '@elastic/eui'; - -interface AutocompleteFieldExistsProps { - placeholder: string; - rowLabel?: string; -} - -export const AutocompleteFieldExistsComponent: React.FC = ({ - placeholder, - rowLabel, -}): JSX.Element => ( - - - -); - -AutocompleteFieldExistsComponent.displayName = 'AutocompleteFieldExists'; diff --git a/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_lists.test.tsx b/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_lists.test.tsx deleted file mode 100644 index 164b8e8d2a6d6..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_lists.test.tsx +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { mount } from 'enzyme'; -import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; -import { waitFor } from '@testing-library/react'; - -import { getField } from '../../../../../../../src/plugins/data/common/index_patterns/fields/fields.mocks'; -import type { ListSchema } from '@kbn/securitysolution-io-ts-list-types'; -import { getFoundListSchemaMock } from '../../../../../lists/common/schemas/response/found_list_schema.mock'; -import { getListResponseMock } from '../../../../../lists/common/schemas/response/list_schema.mock'; -import { DATE_NOW, VERSION, IMMUTABLE } from '../../../../../lists/common/constants.mock'; - -import { AutocompleteFieldListsComponent } from './field_value_lists'; - -jest.mock('../../../common/lib/kibana'); -const mockStart = jest.fn(); -const mockKeywordList: ListSchema = { - ...getListResponseMock(), - id: 'keyword_list', - type: 'keyword', - name: 'keyword list', -}; -const mockResult = { ...getFoundListSchemaMock() }; -mockResult.data = [...mockResult.data, mockKeywordList]; -jest.mock('@kbn/securitysolution-list-hooks', () => { - const originalModule = jest.requireActual('@kbn/securitysolution-list-hooks'); - - return { - ...originalModule, - useFindLists: () => ({ - loading: false, - start: mockStart.mockReturnValue(mockResult), - result: mockResult, - error: undefined, - }), - }; -}); - -describe('AutocompleteFieldListsComponent', () => { - test('it renders disabled if "isDisabled" is true', async () => { - const wrapper = mount( - - ); - - expect( - wrapper - .find(`[data-test-subj="valuesAutocompleteComboBox listsComboxBox"] input`) - .prop('disabled') - ).toBeTruthy(); - }); - - test('it renders loading if "isLoading" is true', async () => { - const wrapper = mount( - - ); - - wrapper - .find(`[data-test-subj="valuesAutocompleteComboBox listsComboxBox"] button`) - .at(0) - .simulate('click'); - expect( - wrapper - .find( - `EuiComboBoxOptionsList[data-test-subj="valuesAutocompleteComboBox listsComboxBox-optionsList"]` - ) - .prop('isLoading') - ).toBeTruthy(); - }); - - test('it allows user to clear values if "isClearable" is true', async () => { - const wrapper = mount( - - ); - expect( - wrapper - .find('EuiComboBox[data-test-subj="valuesAutocompleteComboBox listsComboxBox"]') - .prop('options') - ).toEqual([{ label: 'some name' }]); - }); - - test('it correctly displays lists that match the selected "keyword" field esType', () => { - const wrapper = mount( - - ); - - wrapper.find('[data-test-subj="comboBoxToggleListButton"] button').simulate('click'); - - expect( - wrapper - .find('EuiComboBox[data-test-subj="valuesAutocompleteComboBox listsComboxBox"]') - .prop('options') - ).toEqual([{ label: 'keyword list' }]); - }); - - test('it correctly displays lists that match the selected "ip" field esType', () => { - const wrapper = mount( - - ); - - wrapper.find('[data-test-subj="comboBoxToggleListButton"] button').simulate('click'); - - expect( - wrapper - .find('EuiComboBox[data-test-subj="valuesAutocompleteComboBox listsComboxBox"]') - .prop('options') - ).toEqual([{ label: 'some name' }]); - }); - - test('it correctly displays selected list', async () => { - const wrapper = mount( - - ); - - expect( - wrapper - .find(`[data-test-subj="valuesAutocompleteComboBox listsComboxBox"] EuiComboBoxPill`) - .at(0) - .text() - ).toEqual('some name'); - }); - - test('it invokes "onChange" when option selected', async () => { - const mockOnChange = jest.fn(); - const wrapper = mount( - - ); - - ((wrapper.find(EuiComboBox).props() as unknown) as { - onChange: (a: EuiComboBoxOptionOption[]) => void; - }).onChange([{ label: 'some name' }]); - - await waitFor(() => { - expect(mockOnChange).toHaveBeenCalledWith({ - created_at: DATE_NOW, - created_by: 'some user', - description: 'some description', - id: 'some-list-id', - meta: {}, - name: 'some name', - tie_breaker_id: '6a76b69d-80df-4ab2-8c3e-85f466b06a0e', - type: 'ip', - updated_at: DATE_NOW, - updated_by: 'some user', - _version: undefined, - version: VERSION, - deserializer: undefined, - serializer: undefined, - immutable: IMMUTABLE, - }); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_lists.tsx b/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_lists.tsx deleted file mode 100644 index e8a3c2e70c75b..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_lists.tsx +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useState, useEffect, useCallback, useMemo } from 'react'; -import { EuiFormRow, EuiComboBoxOptionOption, EuiComboBox } from '@elastic/eui'; - -import type { ListSchema } from '@kbn/securitysolution-io-ts-list-types'; -import { useFindLists } from '@kbn/securitysolution-list-hooks'; -import { IFieldType } from '../../../../../../../src/plugins/data/common'; -import { useKibana } from '../../../common/lib/kibana'; -import { filterFieldToList, getGenericComboBoxProps } from './helpers'; -import * as i18n from './translations'; - -interface AutocompleteFieldListsProps { - placeholder: string; - selectedField: IFieldType | undefined; - selectedValue: string | undefined; - isLoading: boolean; - isDisabled: boolean; - isClearable: boolean; - isRequired?: boolean; - rowLabel?: string; - onChange: (arg: ListSchema) => void; -} - -export const AutocompleteFieldListsComponent: React.FC = ({ - placeholder, - rowLabel, - selectedField, - selectedValue, - isLoading = false, - isDisabled = false, - isClearable = false, - isRequired = false, - onChange, -}): JSX.Element => { - const [error, setError] = useState(undefined); - const { http } = useKibana().services; - const [lists, setLists] = useState([]); - const { loading, result, start } = useFindLists(); - const getLabel = useCallback(({ name }) => name, []); - - const optionsMemo = useMemo(() => filterFieldToList(lists, selectedField), [ - lists, - selectedField, - ]); - const selectedOptionsMemo = useMemo(() => { - if (selectedValue != null) { - const list = lists.filter(({ id }) => id === selectedValue); - return list ?? []; - } else { - return []; - } - }, [selectedValue, lists]); - const { comboOptions, labels, selectedComboOptions } = useMemo( - () => - getGenericComboBoxProps({ - options: optionsMemo, - selectedOptions: selectedOptionsMemo, - getLabel, - }), - [optionsMemo, selectedOptionsMemo, getLabel] - ); - - const handleValuesChange = useCallback( - (newOptions: EuiComboBoxOptionOption[]) => { - const [newValue] = newOptions.map(({ label }) => optionsMemo[labels.indexOf(label)]); - onChange(newValue ?? ''); - }, - [labels, optionsMemo, onChange] - ); - - const setIsTouchedValue = useCallback((): void => { - setError(selectedValue == null ? i18n.FIELD_REQUIRED_ERR : undefined); - }, [selectedValue]); - - useEffect(() => { - if (result != null) { - setLists(result.data); - } - }, [result]); - - useEffect(() => { - if (selectedField != null) { - start({ - http, - pageIndex: 1, - pageSize: 500, - }); - } - }, [selectedField, start, http]); - - const isLoadingState = useMemo((): boolean => isLoading || loading, [isLoading, loading]); - - return ( - - - - ); -}; - -AutocompleteFieldListsComponent.displayName = 'AutocompleteFieldList'; diff --git a/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_match.tsx b/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_match.tsx index 9cb219e7a8d45..21d1d9b4b31aa 100644 --- a/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_match.tsx +++ b/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_match.tsx @@ -38,6 +38,11 @@ interface AutocompleteFieldMatchProps { onError?: (arg: boolean) => void; } +/** + * There is a copy of this within: + * x-pack/plugins/lists/public/exceptions/components/autocomplete/field_value_match.tsx + * TODO: This should be in its own packaged and not copied, https://github.com/elastic/kibana/issues/105378 + */ export const AutocompleteFieldMatchComponent: React.FC = ({ placeholder, rowLabel, diff --git a/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_match_any.test.tsx b/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_match_any.test.tsx deleted file mode 100644 index 6b479c5ab8c4c..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_match_any.test.tsx +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { mount, ReactWrapper } from 'enzyme'; -import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; -import { act } from '@testing-library/react'; - -import { - fields, - getField, -} from '../../../../../../../src/plugins/data/common/index_patterns/fields/fields.mocks'; -import { AutocompleteFieldMatchAnyComponent } from './field_value_match_any'; -import { useFieldValueAutocomplete } from './hooks/use_field_value_autocomplete'; - -jest.mock('./hooks/use_field_value_autocomplete'); - -describe('AutocompleteFieldMatchAnyComponent', () => { - let wrapper: ReactWrapper; - const getValueSuggestionsMock = jest - .fn() - .mockResolvedValue([false, true, ['value 3', 'value 4'], jest.fn()]); - - beforeEach(() => { - (useFieldValueAutocomplete as jest.Mock).mockReturnValue([ - false, - true, - ['value 1', 'value 2'], - getValueSuggestionsMock, - ]); - }); - - afterEach(() => { - jest.clearAllMocks(); - wrapper.unmount(); - }); - - test('it renders disabled if "isDisabled" is true', () => { - wrapper = mount( - - ); - - expect( - wrapper.find(`[data-test-subj="valuesAutocompleteMatchAny"] input`).prop('disabled') - ).toBeTruthy(); - }); - - test('it renders loading if "isLoading" is true', () => { - wrapper = mount( - - ); - wrapper.find(`[data-test-subj="valuesAutocompleteMatchAny"] button`).at(0).simulate('click'); - expect( - wrapper - .find(`EuiComboBoxOptionsList[data-test-subj="valuesAutocompleteMatchAny-optionsList"]`) - .prop('isLoading') - ).toBeTruthy(); - }); - - test('it allows user to clear values if "isClearable" is true', () => { - wrapper = mount( - - ); - - expect( - wrapper - .find(`[data-test-subj="comboBoxInput"]`) - .hasClass('euiComboBox__inputWrap-isClearable') - ).toBeTruthy(); - }); - - test('it correctly displays selected value', () => { - wrapper = mount( - - ); - - expect( - wrapper.find(`[data-test-subj="valuesAutocompleteMatchAny"] EuiComboBoxPill`).at(0).text() - ).toEqual('126.45.211.34'); - }); - - test('it invokes "onChange" when new value created', async () => { - const mockOnChange = jest.fn(); - wrapper = mount( - - ); - - ((wrapper.find(EuiComboBox).props() as unknown) as { - onCreateOption: (a: string) => void; - }).onCreateOption('126.45.211.34'); - - expect(mockOnChange).toHaveBeenCalledWith(['126.45.211.34']); - }); - - test('it invokes "onChange" when new value selected', async () => { - const mockOnChange = jest.fn(); - wrapper = mount( - - ); - - ((wrapper.find(EuiComboBox).props() as unknown) as { - onChange: (a: EuiComboBoxOptionOption[]) => void; - }).onChange([{ label: 'value 1' }]); - - expect(mockOnChange).toHaveBeenCalledWith(['value 1']); - }); - - test('it refreshes autocomplete with search query when new value searched', () => { - wrapper = mount( - - ); - act(() => { - ((wrapper.find(EuiComboBox).props() as unknown) as { - onSearchChange: (a: string) => void; - }).onSearchChange('value 1'); - }); - - expect(useFieldValueAutocomplete).toHaveBeenCalledWith({ - selectedField: getField('machine.os.raw'), - operatorType: 'match_any', - query: 'value 1', - fieldValue: [], - indexPattern: { - id: '1234', - title: 'logstash-*', - fields, - }, - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_match_any.tsx b/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_match_any.tsx deleted file mode 100644 index dbfdaf9749b6d..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_match_any.tsx +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useState, useCallback, useMemo } from 'react'; -import { EuiFormRow, EuiComboBoxOptionOption, EuiComboBox } from '@elastic/eui'; -import { uniq } from 'lodash'; - -import { ListOperatorTypeEnum as OperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; -import { IFieldType, IIndexPattern } from '../../../../../../../src/plugins/data/common'; -import { useFieldValueAutocomplete } from './hooks/use_field_value_autocomplete'; -import { getGenericComboBoxProps, paramIsValid } from './helpers'; -import { GetGenericComboBoxPropsReturn } from './types'; - -import * as i18n from './translations'; - -interface AutocompleteFieldMatchAnyProps { - placeholder: string; - selectedField: IFieldType | undefined; - selectedValue: string[]; - indexPattern: IIndexPattern | undefined; - isLoading: boolean; - isDisabled: boolean; - isClearable: boolean; - isRequired?: boolean; - rowLabel?: string; - onChange: (arg: string[]) => void; - onError?: (arg: boolean) => void; -} - -export const AutocompleteFieldMatchAnyComponent: React.FC = ({ - placeholder, - rowLabel, - selectedField, - selectedValue, - indexPattern, - isLoading, - isDisabled = false, - isClearable = false, - isRequired = false, - onChange, - onError, -}): JSX.Element => { - const [searchQuery, setSearchQuery] = useState(''); - const [touched, setIsTouched] = useState(false); - const [error, setError] = useState(undefined); - const [isLoadingSuggestions, isSuggestingValues, suggestions] = useFieldValueAutocomplete({ - selectedField, - operatorType: OperatorTypeEnum.MATCH_ANY, - fieldValue: selectedValue, - query: searchQuery, - indexPattern, - }); - const getLabel = useCallback((option: string): string => option, []); - const optionsMemo = useMemo( - (): string[] => (selectedValue ? uniq([...selectedValue, ...suggestions]) : suggestions), - [suggestions, selectedValue] - ); - const { comboOptions, labels, selectedComboOptions } = useMemo( - (): GetGenericComboBoxPropsReturn => - getGenericComboBoxProps({ - options: optionsMemo, - selectedOptions: selectedValue, - getLabel, - }), - [optionsMemo, selectedValue, getLabel] - ); - - const handleError = useCallback( - (err: string | undefined): void => { - setError((existingErr): string | undefined => { - const oldErr = existingErr != null; - const newErr = err != null; - if (oldErr !== newErr && onError != null) { - onError(newErr); - } - - return err; - }); - }, - [setError, onError] - ); - - const handleValuesChange = useCallback( - (newOptions: EuiComboBoxOptionOption[]): void => { - const newValues: string[] = newOptions.map(({ label }) => optionsMemo[labels.indexOf(label)]); - handleError(undefined); - onChange(newValues); - }, - [handleError, labels, onChange, optionsMemo] - ); - - const handleSearchChange = useCallback( - (searchVal: string) => { - if (searchVal === '') { - handleError(undefined); - } - - if (searchVal !== '' && selectedField != null) { - const err = paramIsValid(searchVal, selectedField, isRequired, touched); - handleError(err); - - setSearchQuery(searchVal); - } - }, - [handleError, isRequired, selectedField, touched] - ); - - const handleCreateOption = useCallback( - (option: string): boolean | void => { - const err = paramIsValid(option, selectedField, isRequired, touched); - handleError(err); - - if (err != null) { - // Explicitly reject the user's input - return false; - } else { - onChange([...(selectedValue || []), option]); - } - }, - [handleError, isRequired, onChange, selectedField, selectedValue, touched] - ); - - const setIsTouchedValue = useCallback((): void => { - handleError(selectedComboOptions.length === 0 ? i18n.FIELD_REQUIRED_ERR : undefined); - setIsTouched(true); - }, [setIsTouched, handleError, selectedComboOptions]); - - const inputPlaceholder = useMemo( - (): string => (isLoading || isLoadingSuggestions ? i18n.LOADING : placeholder), - [isLoading, isLoadingSuggestions, placeholder] - ); - - const isLoadingState = useMemo((): boolean => isLoading || isLoadingSuggestions, [ - isLoading, - isLoadingSuggestions, - ]); - - const defaultInput = useMemo((): JSX.Element => { - return ( - - - - ); - }, [ - comboOptions, - error, - handleCreateOption, - handleSearchChange, - handleValuesChange, - inputPlaceholder, - isClearable, - isDisabled, - isLoadingState, - rowLabel, - selectedComboOptions, - selectedField, - setIsTouchedValue, - ]); - - if (!isSuggestingValues && selectedField != null) { - switch (selectedField.type) { - case 'number': - return ( - - - - ); - default: - return defaultInput; - } - } - - return defaultInput; -}; - -AutocompleteFieldMatchAnyComponent.displayName = 'AutocompleteFieldMatchAny'; diff --git a/x-pack/plugins/security_solution/public/common/components/autocomplete/helpers.test.ts b/x-pack/plugins/security_solution/public/common/components/autocomplete/helpers.test.ts index ae695bf7be978..1618de245365d 100644 --- a/x-pack/plugins/security_solution/public/common/components/autocomplete/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/autocomplete/helpers.test.ts @@ -8,65 +8,13 @@ import moment from 'moment'; import '../../../common/mock/match_media'; import { getField } from '../../../../../../../src/plugins/data/common/index_patterns/fields/fields.mocks'; -import { IFieldType } from '../../../../../../../src/plugins/data/common'; import * as i18n from './translations'; -import { - EXCEPTION_OPERATORS, - isOperator, - isNotOperator, - existsOperator, - doesNotExistOperator, -} from '@kbn/securitysolution-list-utils'; -import { - getOperators, - checkEmptyValue, - paramIsValid, - getGenericComboBoxProps, - typeMatch, - filterFieldToList, -} from './helpers'; -import { getListResponseMock } from '../../../../../lists/common/schemas/response/list_schema.mock'; -import type { ListSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { checkEmptyValue, paramIsValid, getGenericComboBoxProps } from './helpers'; describe('helpers', () => { // @ts-ignore moment.suppressDeprecationWarnings = true; - describe('#getOperators', () => { - test('it returns "isOperator" if passed in field is "undefined"', () => { - const operator = getOperators(undefined); - - expect(operator).toEqual([isOperator]); - }); - - test('it returns expected operators when field type is "boolean"', () => { - const operator = getOperators(getField('ssl')); - - expect(operator).toEqual([isOperator, isNotOperator, existsOperator, doesNotExistOperator]); - }); - - test('it returns "isOperator" when field type is "nested"', () => { - const operator = getOperators({ - name: 'nestedField', - type: 'nested', - esTypes: ['text'], - count: 0, - scripted: false, - searchable: true, - aggregatable: false, - readFromDocValues: false, - subType: { nested: { path: 'nestedField' } }, - }); - - expect(operator).toEqual([isOperator]); - }); - - test('it returns all operator types when field type is not null, boolean, or nested', () => { - const operator = getOperators(getField('machine.os.raw')); - - expect(operator).toEqual(EXCEPTION_OPERATORS); - }); - }); describe('#checkEmptyValue', () => { test('returns no errors if no field has been selected', () => { @@ -272,117 +220,4 @@ describe('helpers', () => { }); }); }); - - describe('#typeMatch', () => { - test('ip -> ip is true', () => { - expect(typeMatch('ip', 'ip')).toEqual(true); - }); - - test('keyword -> keyword is true', () => { - expect(typeMatch('keyword', 'keyword')).toEqual(true); - }); - - test('text -> text is true', () => { - expect(typeMatch('text', 'text')).toEqual(true); - }); - - test('ip_range -> ip is true', () => { - expect(typeMatch('ip_range', 'ip')).toEqual(true); - }); - - test('date_range -> date is true', () => { - expect(typeMatch('date_range', 'date')).toEqual(true); - }); - - test('double_range -> double is true', () => { - expect(typeMatch('double_range', 'double')).toEqual(true); - }); - - test('float_range -> float is true', () => { - expect(typeMatch('float_range', 'float')).toEqual(true); - }); - - test('integer_range -> integer is true', () => { - expect(typeMatch('integer_range', 'integer')).toEqual(true); - }); - - test('long_range -> long is true', () => { - expect(typeMatch('long_range', 'long')).toEqual(true); - }); - - test('ip -> date is false', () => { - expect(typeMatch('ip', 'date')).toEqual(false); - }); - - test('long -> float is false', () => { - expect(typeMatch('long', 'float')).toEqual(false); - }); - - test('integer -> long is false', () => { - expect(typeMatch('integer', 'long')).toEqual(false); - }); - }); - - describe('#filterFieldToList', () => { - test('it returns empty array if given a undefined for field', () => { - const filter = filterFieldToList([], undefined); - expect(filter).toEqual([]); - }); - - test('it returns empty array if filed does not contain esTypes', () => { - const field: IFieldType = { name: 'some-name', type: 'some-type' }; - const filter = filterFieldToList([], field); - expect(filter).toEqual([]); - }); - - test('it returns single filtered list of ip_range -> ip', () => { - const field: IFieldType = { name: 'some-name', type: 'ip', esTypes: ['ip'] }; - const listItem: ListSchema = { ...getListResponseMock(), type: 'ip_range' }; - const filter = filterFieldToList([listItem], field); - const expected: ListSchema[] = [listItem]; - expect(filter).toEqual(expected); - }); - - test('it returns single filtered list of ip -> ip', () => { - const field: IFieldType = { name: 'some-name', type: 'ip', esTypes: ['ip'] }; - const listItem: ListSchema = { ...getListResponseMock(), type: 'ip' }; - const filter = filterFieldToList([listItem], field); - const expected: ListSchema[] = [listItem]; - expect(filter).toEqual(expected); - }); - - test('it returns single filtered list of keyword -> keyword', () => { - const field: IFieldType = { name: 'some-name', type: 'keyword', esTypes: ['keyword'] }; - const listItem: ListSchema = { ...getListResponseMock(), type: 'keyword' }; - const filter = filterFieldToList([listItem], field); - const expected: ListSchema[] = [listItem]; - expect(filter).toEqual(expected); - }); - - test('it returns single filtered list of text -> text', () => { - const field: IFieldType = { name: 'some-name', type: 'text', esTypes: ['text'] }; - const listItem: ListSchema = { ...getListResponseMock(), type: 'text' }; - const filter = filterFieldToList([listItem], field); - const expected: ListSchema[] = [listItem]; - expect(filter).toEqual(expected); - }); - - test('it returns 2 filtered lists of ip_range -> ip', () => { - const field: IFieldType = { name: 'some-name', type: 'ip', esTypes: ['ip'] }; - const listItem1: ListSchema = { ...getListResponseMock(), type: 'ip_range' }; - const listItem2: ListSchema = { ...getListResponseMock(), type: 'ip_range' }; - const filter = filterFieldToList([listItem1, listItem2], field); - const expected: ListSchema[] = [listItem1, listItem2]; - expect(filter).toEqual(expected); - }); - - test('it returns 1 filtered lists of ip_range -> ip if the 2nd is not compatible type', () => { - const field: IFieldType = { name: 'some-name', type: 'ip', esTypes: ['ip'] }; - const listItem1: ListSchema = { ...getListResponseMock(), type: 'ip_range' }; - const listItem2: ListSchema = { ...getListResponseMock(), type: 'text' }; - const filter = filterFieldToList([listItem1, listItem2], field); - const expected: ListSchema[] = [listItem1]; - expect(filter).toEqual(expected); - }); - }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/autocomplete/helpers.ts b/x-pack/plugins/security_solution/public/common/components/autocomplete/helpers.ts index 81f5a66238567..890f1e6755834 100644 --- a/x-pack/plugins/security_solution/public/common/components/autocomplete/helpers.ts +++ b/x-pack/plugins/security_solution/public/common/components/autocomplete/helpers.ts @@ -8,46 +8,17 @@ import dateMath from '@elastic/datemath'; import { EuiComboBoxOptionOption } from '@elastic/eui'; -import type { Type, ListSchema } from '@kbn/securitysolution-io-ts-list-types'; -import { - EXCEPTION_OPERATORS, - isOperator, - isNotOperator, - existsOperator, - doesNotExistOperator, -} from '@kbn/securitysolution-list-utils'; import { IFieldType } from '../../../../../../../src/plugins/data/common'; -import { GetGenericComboBoxPropsReturn, OperatorOption } from './types'; +import { GetGenericComboBoxPropsReturn } from './types'; import * as i18n from './translations'; -/** - * Returns the appropriate operators given a field type - * - * @param field IFieldType selected field - * - */ -export const getOperators = (field: IFieldType | undefined): OperatorOption[] => { - if (field == null) { - return [isOperator]; - } else if (field.type === 'boolean') { - return [isOperator, isNotOperator, existsOperator, doesNotExistOperator]; - } else if (field.type === 'nested') { - return [isOperator]; - } else { - return EXCEPTION_OPERATORS; - } -}; - /** * Determines if empty value is ok + * There is a copy within: + * x-pack/plugins/lists/public/exceptions/components/autocomplete/helpers.ts * - * @param param the value being checked - * @param field the selected field - * @param isRequired whether or not an empty value is allowed - * @param touched has field been touched by user - * @returns undefined if valid, string with error message if invalid, - * null if no checks matched + * TODO: This should be in its own packaged and not copied, https://github.com/elastic/kibana/issues/105378 */ export const checkEmptyValue = ( param: string | undefined, @@ -72,7 +43,10 @@ export const checkEmptyValue = ( /** * Very basic validation for values + * There is a copy within: + * x-pack/plugins/lists/public/exceptions/components/autocomplete/helpers.ts * + * TODO: This should be in its own packaged and not copied, https://github.com/elastic/kibana/issues/105378 * @param param the value being checked * @param field the selected field * @param isRequired whether or not an empty value is allowed @@ -109,7 +83,10 @@ export const paramIsValid = ( /** * Determines the options, selected values and option labels for EUI combo box + * There is a copy within: + * x-pack/plugins/lists/public/exceptions/components/autocomplete/helpers.ts * + * TODO: This should be in its own packaged and not copied, https://github.com/elastic/kibana/issues/105378 * @param options options user can select from * @param selectedOptions user selection if any * @param getLabel helper function to know which property to use for labels @@ -140,36 +117,3 @@ export function getGenericComboBoxProps({ selectedComboOptions: newSelectedComboOptions, }; } - -/** - * Given an array of lists and optionally a field this will return all - * the lists that match against the field based on the types from the field - * @param lists The lists to match against the field - * @param field The field to check against the list to see if they are compatible - */ -export const filterFieldToList = (lists: ListSchema[], field?: IFieldType): ListSchema[] => { - if (field != null) { - const { esTypes = [] } = field; - return lists.filter(({ type }) => esTypes.some((esType) => typeMatch(type, esType))); - } else { - return []; - } -}; - -/** - * Given an input list type and a string based ES type this will match - * if they're exact or if they are compatible with a range - * @param type The type to match against the esType - * @param esType The ES type to match with - */ -export const typeMatch = (type: Type, esType: string): boolean => { - return ( - type === esType || - (type === 'ip_range' && esType === 'ip') || - (type === 'date_range' && esType === 'date') || - (type === 'double_range' && esType === 'double') || - (type === 'float_range' && esType === 'float') || - (type === 'integer_range' && esType === 'integer') || - (type === 'long_range' && esType === 'long') - ); -}; diff --git a/x-pack/plugins/security_solution/public/common/components/autocomplete/hooks/use_field_value_autocomplete.ts b/x-pack/plugins/security_solution/public/common/components/autocomplete/hooks/use_field_value_autocomplete.ts index 0f369fa01d01e..0fc4a663b7e11 100644 --- a/x-pack/plugins/security_solution/public/common/components/autocomplete/hooks/use_field_value_autocomplete.ts +++ b/x-pack/plugins/security_solution/public/common/components/autocomplete/hooks/use_field_value_autocomplete.ts @@ -30,9 +30,13 @@ export interface UseFieldValueAutocompleteProps { query: string; indexPattern: IIndexPattern | undefined; } + /** * Hook for using the field value autocomplete service + * There is a copy within: + * x-pack/plugins/lists/public/exceptions/components/autocomplete/hooks.ts * + * TODO: This should be in its own packaged and not copied, https://github.com/elastic/kibana/issues/105378 */ export const useFieldValueAutocomplete = ({ selectedField, diff --git a/x-pack/plugins/security_solution/public/common/components/autocomplete/operator.test.tsx b/x-pack/plugins/security_solution/public/common/components/autocomplete/operator.test.tsx deleted file mode 100644 index 5e00d2beb571c..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/autocomplete/operator.test.tsx +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { mount } from 'enzyme'; -import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; - -import { getField } from '../../../../../../../src/plugins/data/common/index_patterns/fields/fields.mocks'; -import { OperatorComponent } from './operator'; -import { isOperator, isNotOperator } from '@kbn/securitysolution-list-utils'; - -describe('OperatorComponent', () => { - test('it renders disabled if "isDisabled" is true', () => { - const wrapper = mount( - - ); - - expect( - wrapper.find(`[data-test-subj="operatorAutocompleteComboBox"] input`).prop('disabled') - ).toBeTruthy(); - }); - - test('it renders loading if "isLoading" is true', () => { - const wrapper = mount( - - ); - wrapper.find(`[data-test-subj="operatorAutocompleteComboBox"] button`).at(0).simulate('click'); - expect( - wrapper - .find(`EuiComboBoxOptionsList[data-test-subj="operatorAutocompleteComboBox-optionsList"]`) - .prop('isLoading') - ).toBeTruthy(); - }); - - test('it allows user to clear values if "isClearable" is true', () => { - const wrapper = mount( - - ); - - expect(wrapper.find(`button[data-test-subj="comboBoxClearButton"]`).exists()).toBeTruthy(); - }); - - test('it displays "operatorOptions" if param is passed in with items', () => { - const wrapper = mount( - - ); - - expect( - wrapper.find(`[data-test-subj="operatorAutocompleteComboBox"]`).at(0).prop('options') - ).toEqual([{ label: 'is not' }]); - }); - - test('it does not display "operatorOptions" if param is passed in with no items', () => { - const wrapper = mount( - - ); - - expect( - wrapper.find(`[data-test-subj="operatorAutocompleteComboBox"]`).at(0).prop('options') - ).toEqual([ - { - label: 'is', - }, - { - label: 'is not', - }, - { - label: 'is one of', - }, - { - label: 'is not one of', - }, - { - label: 'exists', - }, - { - label: 'does not exist', - }, - { - label: 'is in list', - }, - { - label: 'is not in list', - }, - ]); - }); - - test('it correctly displays selected operator', () => { - const wrapper = mount( - - ); - - expect( - wrapper.find(`[data-test-subj="operatorAutocompleteComboBox"] EuiComboBoxPill`).at(0).text() - ).toEqual('is'); - }); - - test('it only displays subset of operators if field type is nested', () => { - const wrapper = mount( - - ); - - expect( - wrapper.find(`[data-test-subj="operatorAutocompleteComboBox"]`).at(0).prop('options') - ).toEqual([{ label: 'is' }]); - }); - - test('it only displays subset of operators if field type is boolean', () => { - const wrapper = mount( - - ); - - expect( - wrapper.find(`[data-test-subj="operatorAutocompleteComboBox"]`).at(0).prop('options') - ).toEqual([ - { label: 'is' }, - { label: 'is not' }, - { label: 'exists' }, - { label: 'does not exist' }, - ]); - }); - - test('it invokes "onChange" when option selected', () => { - const mockOnChange = jest.fn(); - const wrapper = mount( - - ); - - ((wrapper.find(EuiComboBox).props() as unknown) as { - onChange: (a: EuiComboBoxOptionOption[]) => void; - }).onChange([{ label: 'is not' }]); - - expect(mockOnChange).toHaveBeenCalledWith([ - { message: 'is not', operator: 'excluded', type: 'match', value: 'is_not' }, - ]); - }); -}); diff --git a/x-pack/plugins/security_solution/public/common/components/autocomplete/operator.tsx b/x-pack/plugins/security_solution/public/common/components/autocomplete/operator.tsx deleted file mode 100644 index d8f49acd04b99..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/autocomplete/operator.tsx +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useCallback, useMemo } from 'react'; -import { EuiComboBoxOptionOption, EuiComboBox } from '@elastic/eui'; - -import { IFieldType } from '../../../../../../../src/plugins/data/common'; -import { getOperators, getGenericComboBoxProps } from './helpers'; -import { GetGenericComboBoxPropsReturn, OperatorOption } from './types'; - -interface OperatorState { - placeholder: string; - selectedField: IFieldType | undefined; - operator: OperatorOption; - isLoading: boolean; - isDisabled: boolean; - isClearable: boolean; - operatorInputWidth?: number; - operatorOptions?: OperatorOption[]; - onChange: (arg: OperatorOption[]) => void; -} - -export const OperatorComponent: React.FC = ({ - placeholder, - selectedField, - operator, - isLoading = false, - isDisabled = false, - isClearable = false, - operatorOptions, - operatorInputWidth = 150, - onChange, -}): JSX.Element => { - const getLabel = useCallback(({ message }): string => message, []); - const optionsMemo = useMemo( - (): OperatorOption[] => - operatorOptions != null && operatorOptions.length > 0 - ? operatorOptions - : getOperators(selectedField), - [operatorOptions, selectedField] - ); - const selectedOptionsMemo = useMemo((): OperatorOption[] => (operator ? [operator] : []), [ - operator, - ]); - const { comboOptions, labels, selectedComboOptions } = useMemo( - (): GetGenericComboBoxPropsReturn => - getGenericComboBoxProps({ - options: optionsMemo, - selectedOptions: selectedOptionsMemo, - getLabel, - }), - [optionsMemo, selectedOptionsMemo, getLabel] - ); - - const handleValuesChange = (newOptions: EuiComboBoxOptionOption[]): void => { - const newValues: OperatorOption[] = newOptions.map( - ({ label }) => optionsMemo[labels.indexOf(label)] - ); - onChange(newValues); - }; - - return ( - - ); -}; - -OperatorComponent.displayName = 'Operator'; diff --git a/x-pack/plugins/security_solution/public/common/components/autocomplete/types.ts b/x-pack/plugins/security_solution/public/common/components/autocomplete/types.ts index 1d8e3e9aee28e..07f1903fb70e1 100644 --- a/x-pack/plugins/security_solution/public/common/components/autocomplete/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/autocomplete/types.ts @@ -7,20 +7,8 @@ import { EuiComboBoxOptionOption } from '@elastic/eui'; -import type { - ListOperatorEnum as OperatorEnum, - ListOperatorTypeEnum as OperatorTypeEnum, -} from '@kbn/securitysolution-io-ts-list-types'; - export interface GetGenericComboBoxPropsReturn { comboOptions: EuiComboBoxOptionOption[]; labels: string[]; selectedComboOptions: EuiComboBoxOptionOption[]; } - -export interface OperatorOption { - message: string; - value: string; - operator: OperatorEnum; - type: OperatorTypeEnum; -} diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/__snapshots__/drag_drop_context_wrapper.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/__snapshots__/drag_drop_context_wrapper.test.tsx.snap index 2dc3f7fd336a2..6ef797580be9b 100644 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/__snapshots__/drag_drop_context_wrapper.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/__snapshots__/drag_drop_context_wrapper.test.tsx.snap @@ -366,6 +366,7 @@ exports[`DragDropContextWrapper rendering it renders against the snapshot 1`] = "format": "", "indexes": Array [ "apm-*-transaction*", + "traces-apm*", "auditbeat-*", "endgame-*", "filebeat-*", diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/action_completion_return_button.tsx b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/action_completion_return_button.tsx new file mode 100644 index 0000000000000..af05999b2de26 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/action_completion_return_button.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback } from 'react'; +import { EuiSpacer, EuiFlexGroup, EuiFlexItem, EuiButtonEmpty, EuiText } from '@elastic/eui'; + +export const ActionCompletionReturnButton = React.memo( + ({ onClick, buttonText }: { onClick: () => void; buttonText: string }) => { + const onClickCallback = useCallback(() => onClick(), [onClick]); + + return ( + <> + + + + + +

{buttonText}

+
+
+
+
+ + ); + } +); + +ActionCompletionReturnButton.displayName = 'ActionCompletionReturnButton'; diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/index.ts b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/index.ts index bd8e23e3a4559..24b94cd6212b7 100644 --- a/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/index.ts +++ b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/index.ts @@ -9,3 +9,4 @@ export * from './isolate_success'; export * from './isolate_form'; export * from './unisolate_form'; export * from './endpoint_host_isolation_status'; +export * from './action_completion_return_button'; diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/isolate_success.tsx b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/isolate_success.tsx index 3459da068b282..7f05e8eafac87 100644 --- a/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/isolate_success.tsx +++ b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/isolate_success.tsx @@ -5,58 +5,84 @@ * 2.0. */ -import React, { memo, ReactNode } from 'react'; -import { EuiButtonEmpty, EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import React, { memo, ReactNode, useMemo } from 'react'; +import { EuiCallOut, EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; import { GET_ISOLATION_SUCCESS_MESSAGE, GET_UNISOLATION_SUCCESS_MESSAGE } from './translations'; +import { useCasesFromAlerts } from '../../../../detections/containers/detection_engine/alerts/use_cases_from_alerts'; +import { CaseDetailsLink } from '../../../../common/components/links'; export interface EndpointIsolateSuccessProps { hostName: string; + alertId?: string; isolateAction?: 'isolateHost' | 'unisolateHost'; - completeButtonLabel: string; - onComplete: () => void; additionalInfo?: ReactNode; } +const CasesAdditionalInfo: React.FC<{ alertIdForCase: string }> = ({ alertIdForCase }) => { + const { casesInfo } = useCasesFromAlerts({ alertId: alertIdForCase }); + + const caseCount: number = useMemo(() => casesInfo.length, [casesInfo]); + + const casesList = useMemo( + () => + casesInfo.map((caseInfo, index) => { + return ( +
  • + + + +
  • + ); + }), + [casesInfo] + ); + + return ( + <> + {caseCount > 0 && ( + <> + +

    + +

    +
    + +
      {casesList}
    +
    + + )} + + ); +}; + export const EndpointIsolateSuccess = memo( - ({ - hostName, - isolateAction = 'isolateHost', - onComplete, - completeButtonLabel, - additionalInfo, - }) => { + ({ hostName, alertId, isolateAction = 'isolateHost', additionalInfo }) => { return ( - <> - - {additionalInfo} - - - - - -

    {completeButtonLabel}

    -
    -
    -
    -
    - + + {alertId !== undefined ? CasesAdditionalInfo({ alertIdForCase: alertId }) : additionalInfo} + ); } ); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/__mocks__/index.ts b/x-pack/plugins/security_solution/public/common/components/event_details/__mocks__/index.ts index 3edd6e6fda14b..620c3991b0ad9 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/public/common/components/event_details/__mocks__/index.ts @@ -406,6 +406,7 @@ export const mockAlertDetailsData = [ field: 'signal.rule.index', values: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -415,6 +416,7 @@ export const mockAlertDetailsData = [ ], originalValue: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/exceptionable_endpoint_fields.json b/x-pack/plugins/security_solution/public/common/components/exceptions/exceptionable_endpoint_fields.json index b5480aac27f67..c37be60545ab2 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/exceptionable_endpoint_fields.json +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/exceptionable_endpoint_fields.json @@ -2,10 +2,12 @@ "Endpoint.policy.applied.id", "Target.process.Ext.services", "Target.process.Ext.user", + "Target.process.executable", "Target.process.hash.md5", "Target.process.hash.sha1", "Target.process.hash.sha256", "Target.process.hash.sha512", + "Target.process.name", "Target.process.parent.hash.md5", "Target.process.parent.hash.sha1", "Target.process.parent.hash.sha256", @@ -17,6 +19,14 @@ "Target.process.pe.original_file_name", "Target.process.pe.product", "Target.process.pgid", + "Target.process.thread.Ext.start_address_details.allocation_type", + "Target.process.thread.Ext.start_address_bytes_disasm_hash", + "Target.process.thread.Ext.start_address_allocation_offset", + "Target.process.thread.Ext.start_address_details.allocation_size", + "Target.process.thread.Ext.start_address_details.region_size", + "Target.process.thread.Ext.start_address_details.region_protection", + "Target.process.thread.Ext.start_address_details.memory_pe.imphash", + "Target.process.thread.Ext.start_address_bytes", "agent.id", "agent.type", "agent.version", @@ -68,10 +78,13 @@ "host.type", "process.Ext.services", "process.Ext.user", + "process.Ext.code_signature", + "process.executable", "process.hash.md5", "process.hash.sha1", "process.hash.sha256", "process.hash.sha512", + "process.name", "process.parent.hash.md5", "process.parent.hash.sha1", "process.parent.hash.sha256", @@ -88,5 +101,7 @@ "user.email", "user.hash", "user.id", - "Ransomware.feature" + "Ransomware.feature", + "Memory_protection.feature", + "Memory_protection.self_injection" ] diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx index 0af83e2cff3b5..32eb4baad5059 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx @@ -928,5 +928,172 @@ describe('Exception helpers', () => { }, ]); }); + + test('it should return pre-populated memory signature items for event code `memory_signature`', () => { + const defaultItems = defaultEndpointExceptionItems('list_id', 'my_rule', { + _id: '123', + process: { + name: 'some name', + executable: 'some file path', + hash: { + sha256: 'some hash', + }, + }, + // eslint-disable-next-line @typescript-eslint/naming-convention + Memory_protection: { + feature: 'signature', + }, + event: { + code: 'memory_signature', + }, + }); + + expect(defaultItems[0].entries).toEqual([ + { + field: 'Memory_protection.feature', + operator: 'included', + type: 'match', + value: 'signature', + id: '123', + }, + { + field: 'process.executable.caseless', + operator: 'included', + type: 'match', + value: 'some file path', + id: '123', + }, + { + field: 'process.name.caseless', + operator: 'included', + type: 'match', + value: 'some name', + id: '123', + }, + { + field: 'process.hash.sha256', + operator: 'included', + type: 'match', + value: 'some hash', + id: '123', + }, + ]); + }); + + test('it should return pre-populated memory shellcode items for event code `malicious_thread`', () => { + const defaultItems = defaultEndpointExceptionItems('list_id', 'my_rule', { + _id: '123', + process: { + name: 'some name', + executable: 'some file path', + Ext: { + token: { + integrity_level_name: 'high', + }, + }, + }, + // eslint-disable-next-line @typescript-eslint/naming-convention + Memory_protection: { + feature: 'shellcode_thread', + self_injection: true, + }, + event: { + code: 'malicious_thread', + }, + Target: { + process: { + thread: { + Ext: { + start_address_allocation_offset: 0, + start_address_bytes_disasm_hash: 'a disam hash', + start_address_details: { + allocation_type: 'PRIVATE', + allocation_size: 4000, + region_size: 4000, + region_protection: 'RWX', + memory_pe: { + imphash: 'a hash', + }, + }, + }, + }, + }, + }, + }); + + expect(defaultItems[0].entries).toEqual([ + { + field: 'Memory_protection.feature', + operator: 'included', + type: 'match', + value: 'shellcode_thread', + id: '123', + }, + { + field: 'Memory_protection.self_injection', + operator: 'included', + type: 'match', + value: 'true', + id: '123', + }, + { + field: 'process.executable.caseless', + operator: 'included', + type: 'match', + value: 'some file path', + id: '123', + }, + { + field: 'process.name.caseless', + operator: 'included', + type: 'match', + value: 'some name', + id: '123', + }, + { + field: 'process.Ext.token.integrity_level_name', + operator: 'included', + type: 'match', + value: 'high', + id: '123', + }, + { + field: 'Target.process.thread.Ext.start_address_details', + type: 'nested', + entries: [ + { + field: 'allocation_type', + operator: 'included', + type: 'match', + value: 'PRIVATE', + id: '123', + }, + { + field: 'allocation_size', + operator: 'included', + type: 'match', + value: '4000', + id: '123', + }, + { field: 'region_size', operator: 'included', type: 'match', value: '4000', id: '123' }, + { + field: 'region_protection', + operator: 'included', + type: 'match', + value: 'RWX', + id: '123', + }, + { + field: 'memory_pe.imphash', + operator: 'included', + type: 'match', + value: 'a hash', + id: '123', + }, + ], + id: '123', + }, + ]); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx index bfb5c7298f330..3c8652637a997 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx @@ -496,6 +496,139 @@ export const getPrepopulatedRansomwareException = ({ }; }; +export const getPrepopulatedMemorySignatureException = ({ + listId, + ruleName, + eventCode, + listNamespace = 'agnostic', + alertEcsData, +}: { + listId: string; + listNamespace?: NamespaceType; + ruleName: string; + eventCode: string; + alertEcsData: Flattened; +}): ExceptionsBuilderExceptionItem => { + const { process } = alertEcsData; + return { + ...getNewExceptionItem({ listId, namespaceType: listNamespace, ruleName }), + entries: addIdToEntries([ + { + field: 'Memory_protection.feature', + operator: 'included', + type: 'match', + value: alertEcsData.Memory_protection?.feature ?? '', + }, + { + field: 'process.executable.caseless', + operator: 'included', + type: 'match', + value: process?.executable ?? '', + }, + { + field: 'process.name.caseless', + operator: 'included', + type: 'match', + value: process?.name ?? '', + }, + { + field: 'process.hash.sha256', + operator: 'included', + type: 'match', + value: process?.hash?.sha256 ?? '', + }, + ]), + }; +}; +export const getPrepopulatedMemoryShellcodeException = ({ + listId, + ruleName, + eventCode, + listNamespace = 'agnostic', + alertEcsData, +}: { + listId: string; + listNamespace?: NamespaceType; + ruleName: string; + eventCode: string; + alertEcsData: Flattened; +}): ExceptionsBuilderExceptionItem => { + const { process, Target } = alertEcsData; + return { + ...getNewExceptionItem({ listId, namespaceType: listNamespace, ruleName }), + entries: addIdToEntries([ + { + field: 'Memory_protection.feature', + operator: 'included', + type: 'match', + value: alertEcsData.Memory_protection?.feature ?? '', + }, + { + field: 'Memory_protection.self_injection', + operator: 'included', + type: 'match', + value: String(alertEcsData.Memory_protection?.self_injection) ?? '', + }, + { + field: 'process.executable.caseless', + operator: 'included', + type: 'match', + value: process?.executable ?? '', + }, + { + field: 'process.name.caseless', + operator: 'included', + type: 'match', + value: process?.name ?? '', + }, + { + field: 'process.Ext.token.integrity_level_name', + operator: 'included', + type: 'match', + value: process?.Ext?.token?.integrity_level_name ?? '', + }, + { + field: 'Target.process.thread.Ext.start_address_details', + type: 'nested', + entries: [ + { + field: 'allocation_type', + operator: 'included', + type: 'match', + value: Target?.process?.thread?.Ext?.start_address_details?.allocation_type ?? '', + }, + { + field: 'allocation_size', + operator: 'included', + type: 'match', + value: + String(Target?.process?.thread?.Ext?.start_address_details?.allocation_size) ?? '', + }, + { + field: 'region_size', + operator: 'included', + type: 'match', + value: String(Target?.process?.thread?.Ext?.start_address_details?.region_size) ?? '', + }, + { + field: 'region_protection', + operator: 'included', + type: 'match', + value: + String(Target?.process?.thread?.Ext?.start_address_details?.region_protection) ?? '', + }, + { + field: 'memory_pe.imphash', + operator: 'included', + type: 'match', + value: + String(Target?.process?.thread?.Ext?.start_address_details?.memory_pe?.imphash) ?? '', + }, + ], + }, + ]), + }; +}; /** * Determines whether or not any entries within the given exceptionItems contain values not in the specified ECS mapping */ @@ -537,26 +670,45 @@ export const defaultEndpointExceptionItems = ( const { event: alertEvent } = alertEcsData; const eventCode = alertEvent?.code ?? ''; - if (eventCode === 'ransomware') { - return getProcessCodeSignature(alertEcsData).map((codeSignature) => - getPrepopulatedRansomwareException({ - listId, - ruleName, - eventCode, - codeSignature, - alertEcsData, - }) - ); + switch (eventCode) { + case 'memory_signature': + return [ + getPrepopulatedMemorySignatureException({ + listId, + ruleName, + eventCode, + alertEcsData, + }), + ]; + case 'malicious_thread': + return [ + getPrepopulatedMemoryShellcodeException({ + listId, + ruleName, + eventCode, + alertEcsData, + }), + ]; + case 'ransomware': + return getProcessCodeSignature(alertEcsData).map((codeSignature) => + getPrepopulatedRansomwareException({ + listId, + ruleName, + eventCode, + codeSignature, + alertEcsData, + }) + ); + default: + // By default return the standard prepopulated Endpoint Exception fields + return getFileCodeSignature(alertEcsData).map((codeSignature) => + getPrepopulatedEndpointException({ + listId, + ruleName, + eventCode, + codeSignature, + alertEcsData, + }) + ); } - - // By default return the standard prepopulated Endpoint Exception fields - return getFileCodeSignature(alertEcsData).map((codeSignature) => - getPrepopulatedEndpointException({ - listId, - ruleName, - eventCode, - codeSignature, - alertEcsData, - }) - ); }; 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 1f7e668b21b98..f415dc287ca35 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 @@ -323,8 +323,7 @@ describe('Navigation Breadcrumbs', () => { }, { text: 'Create', - href: - "securitySolution/rules/create?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", + href: '', }, ]); }); @@ -382,7 +381,7 @@ describe('Navigation Breadcrumbs', () => { }, { text: 'Edit', - href: `securitySolution/rules/id/${mockDetailName}/edit?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))`, + href: '', }, ]); }); diff --git a/x-pack/plugins/security_solution/public/common/components/sourcerer/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/sourcerer/index.test.tsx index e8f382a5050d8..87a7ce805940f 100644 --- a/x-pack/plugins/security_solution/public/common/components/sourcerer/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/sourcerer/index.test.tsx @@ -39,6 +39,7 @@ const mockOptions = [ { label: 'filebeat-*', value: 'filebeat-*' }, { label: 'logs-*', value: 'logs-*' }, { label: 'packetbeat-*', value: 'packetbeat-*' }, + { label: 'traces-apm*', value: 'traces-apm*' }, { label: 'winlogbeat-*', value: 'winlogbeat-*' }, ]; diff --git a/x-pack/plugins/security_solution/public/common/store/sourcerer/selectors.test.ts b/x-pack/plugins/security_solution/public/common/store/sourcerer/selectors.test.ts index 730857b6494d9..dd608138ef9f0 100644 --- a/x-pack/plugins/security_solution/public/common/store/sourcerer/selectors.test.ts +++ b/x-pack/plugins/security_solution/public/common/store/sourcerer/selectors.test.ts @@ -23,6 +23,7 @@ describe('Sourcerer selectors', () => { 'filebeat-*', 'logs-*', 'packetbeat-*', + 'traces-apm*', 'winlogbeat-*', '-*elastic-cloud-logs-*', ]); @@ -42,6 +43,7 @@ describe('Sourcerer selectors', () => { 'endgame-*', 'filebeat-*', 'packetbeat-*', + 'traces-apm*', 'winlogbeat-*', ]); }); @@ -64,6 +66,7 @@ describe('Sourcerer selectors', () => { 'filebeat-*', 'logs-endpoint.event-*', 'packetbeat-*', + 'traces-apm*', 'winlogbeat-*', ]); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/host_isolation/index.tsx b/x-pack/plugins/security_solution/public/detections/components/host_isolation/index.tsx index 36443cc91f4e8..acbae367fe75e 100644 --- a/x-pack/plugins/security_solution/public/detections/components/host_isolation/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/host_isolation/index.tsx @@ -7,11 +7,8 @@ import React, { useMemo } from 'react'; import { find } from 'lodash/fp'; -import { EuiText } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; import { Maybe } from '../../../../../observability/common/typings'; import { useCasesFromAlerts } from '../../containers/detection_engine/alerts/use_cases_from_alerts'; -import { CaseDetailsLink } from '../../../common/components/links'; import { TimelineEventsDetailsItem } from '../../../../common/search_strategy'; import { IsolateHost } from './isolate'; import { UnisolateHost } from './unisolate'; @@ -45,53 +42,10 @@ export const HostIsolationPanel = React.memo( const { casesInfo } = useCasesFromAlerts({ alertId }); - // Cases related components to be used in both isolate and unisolate actions from the alert details flyout entry point - const caseCount: number = useMemo(() => casesInfo.length, [casesInfo]); - - const casesList = useMemo( - () => - casesInfo.map((caseInfo, index) => { - return ( -
  • - - - -
  • - ); - }), - [casesInfo] - ); - - const associatedCases = useMemo(() => { - if (caseCount > 0) { - return ( - <> - -

    - -

    -
    - -
      {casesList}
    -
    - - ); - } - }, [caseCount, casesList]); - return isolateAction === 'isolateHost' ? ( void; successCallback?: () => void; @@ -60,20 +58,14 @@ export const IsolateHost = React.memo( const caseCount: number = useMemo(() => casesInfo.length, [casesInfo]); - const hostIsolatedSuccess = useMemo(() => { + const hostIsolatedSuccessButton = useMemo(() => { return ( - <> - - - + ); - }, [backToAlertDetails, hostName, cases]); + }, [backToAlertDetails]); const hostNotIsolated = useMemo(() => { return ( @@ -108,7 +100,7 @@ export const IsolateHost = React.memo( caseCount, ]); - return isIsolated ? hostIsolatedSuccess : hostNotIsolated; + return isIsolated ? hostIsolatedSuccessButton : hostNotIsolated; } ); diff --git a/x-pack/plugins/security_solution/public/detections/components/host_isolation/unisolate.tsx b/x-pack/plugins/security_solution/public/detections/components/host_isolation/unisolate.tsx index 2b810dc16eec8..5cc862694bef8 100644 --- a/x-pack/plugins/security_solution/public/detections/components/host_isolation/unisolate.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/host_isolation/unisolate.tsx @@ -5,14 +5,14 @@ * 2.0. */ -import React, { useMemo, useState, useCallback, ReactNode } from 'react'; +import React, { useMemo, useState, useCallback } from 'react'; import { EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { CASES_ASSOCIATED_WITH_ALERT, RETURN_TO_ALERT_DETAILS } from './translations'; import { EndpointIsolatedFormProps, - EndpointIsolateSuccess, EndpointUnisolateForm, + ActionCompletionReturnButton, } from '../../../common/components/endpoint/host_isolation'; import { useHostUnisolation } from '../../containers/detection_engine/alerts/use_host_unisolation'; import { CasesFromAlertsResponse } from '../../containers/detection_engine/alerts/types'; @@ -21,14 +21,12 @@ export const UnisolateHost = React.memo( ({ endpointId, hostName, - cases, casesInfo, cancelCallback, successCallback, }: { endpointId: string; hostName: string; - cases: ReactNode; casesInfo: CasesFromAlertsResponse; cancelCallback: () => void; successCallback?: () => void; @@ -60,20 +58,14 @@ export const UnisolateHost = React.memo( const caseCount: number = useMemo(() => casesInfo.length, [casesInfo]); - const hostUnisolatedSuccess = useMemo(() => { + const hostUnisolatedSuccessButton = useMemo(() => { return ( - <> - - - + ); - }, [backToAlertDetails, hostName, cases]); + }, [backToAlertDetails]); const hostNotUnisolated = useMemo(() => { return ( @@ -108,7 +100,7 @@ export const UnisolateHost = React.memo( caseCount, ]); - return isUnIsolated ? hostUnisolatedSuccess : hostNotUnisolated; + return isUnIsolated ? hostUnisolatedSuccessButton : hostNotUnisolated; } ); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/schema.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/schema.tsx index 3467b34d47135..1dd59d49e4ff5 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/schema.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/schema.tsx @@ -13,6 +13,7 @@ import { FormSchema, ValidationFunc, ERROR_CODE, + VALIDATION_TYPES, } from '../../../../shared_imports'; import { AboutStepRule } from '../../../pages/detection_engine/rules/types'; import { OptionalFieldLabel } from '../optional_field_label'; @@ -38,6 +39,20 @@ export const schema: FormSchema = { } ), labelAppend: OptionalFieldLabel, + validations: [ + { + validator: emptyField( + i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepAboutRule.authorFieldEmptyError', + { + defaultMessage: 'An author must not be empty', + } + ) + ), + type: VALIDATION_TYPES.ARRAY_ITEM, + isBlocking: false, + }, + ], }, name: { type: FIELD_TYPES.TEXT, @@ -243,6 +258,20 @@ export const schema: FormSchema = { } ), labelAppend: OptionalFieldLabel, + validations: [ + { + validator: emptyField( + i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepAboutRule.tagFieldEmptyError', + { + defaultMessage: 'A tag must not be empty', + } + ) + ), + type: VALIDATION_TYPES.ARRAY_ITEM, + isBlocking: false, + }, + ], }, note: { type: FIELD_TYPES.TEXTAREA, diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/mock.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/mock.ts index e4bddfba8278b..7aba8fa4ac10f 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/mock.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/mock.ts @@ -175,6 +175,7 @@ export const alertsMock: AlertSearchResponse = { immutable: false, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -414,6 +415,7 @@ export const alertsMock: AlertSearchResponse = { immutable: false, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -619,6 +621,7 @@ export const alertsMock: AlertSearchResponse = { immutable: false, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -822,6 +825,7 @@ export const alertsMock: AlertSearchResponse = { immutable: false, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/mock.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/mock.ts index 1104cb86064b0..533ab6138cb09 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/mock.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/mock.ts @@ -20,6 +20,7 @@ export const savedRuleMock: Rule = { id: '12345678987654321', index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule.test.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule.test.tsx index ca6cd5b11f705..096463872fc01 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule.test.tsx @@ -54,6 +54,7 @@ describe('useRule', () => { immutable: false, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_status.test.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_status.test.tsx index 3394f1fc553ae..4d01e2ff00ec1 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_status.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_status.test.tsx @@ -43,6 +43,7 @@ const testRule: Rule = { immutable: false, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_with_fallback.test.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_with_fallback.test.tsx index abd5a2781c8a7..1f08a35660215 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_with_fallback.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_with_fallback.test.tsx @@ -62,6 +62,7 @@ describe('useRuleWithFallback', () => { "immutable": false, "index": Array [ "apm-*-transaction*", + "traces-apm*", "auditbeat-*", "endgame-*", "filebeat-*", @@ -125,6 +126,7 @@ describe('useRuleWithFallback', () => { "immutable": false, "index": Array [ "apm-*-transaction*", + "traces-apm*", "auditbeat-*", "endgame-*", "filebeat-*", diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/utils.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/utils.ts index bbc085eaa0be8..92c828b6cbf79 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/utils.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/utils.ts @@ -11,8 +11,6 @@ import { ChromeBreadcrumb } from '../../../../../../../../src/core/public'; import { getRulesUrl, getRuleDetailsUrl, - getCreateRuleUrl, - getEditRuleUrl, } from '../../../../common/components/link_to/redirect_to_detection_engine'; import * as i18nRules from './translations'; import { RouteSpyState } from '../../../../common/utils/route/types'; @@ -79,10 +77,7 @@ export const getBreadcrumbs = ( ...breadcrumb, { text: i18nRules.ADD_PAGE_TITLE, - href: getUrlForApp(APP_ID, { - deepLinkId: SecurityPageName.rules, - path: getCreateRuleUrl(!isEmpty(search[0]) ? search[0] : ''), - }), + href: '', }, ]; } @@ -92,10 +87,7 @@ export const getBreadcrumbs = ( ...breadcrumb, { text: i18nRules.EDIT_PAGE_TITLE, - href: getUrlForApp(APP_ID, { - deepLinkId: SecurityPageName.rules, - path: getEditRuleUrl(params.detailName, !isEmpty(search[0]) ? search[0] : ''), - }), + href: '', }, ]; } diff --git a/x-pack/plugins/security_solution/public/management/common/routing.ts b/x-pack/plugins/security_solution/public/management/common/routing.ts index 9cb25dd4bb5a5..e6c02ffefa3c5 100644 --- a/x-pack/plugins/security_solution/public/management/common/routing.ts +++ b/x-pack/plugins/security_solution/public/management/common/routing.ts @@ -81,6 +81,9 @@ export const getEndpointDetailsPath = ( const queryParams: EndpointDetailsUrlProps = { ...rest }; switch (props.name) { + case 'endpointDetails': + queryParams.show = 'details'; + break; case 'endpointIsolate': queryParams.show = 'isolate'; break; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts index 3fe6821abbcbe..b92a1958a57ac 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts @@ -15,7 +15,7 @@ import { } from '../../../../../common/endpoint/types'; import { ServerApiError } from '../../../../common/types'; import { GetPolicyListResponse } from '../../policy/types'; -import { EndpointIndexUIQueryParams, EndpointState } from '../types'; +import { EndpointState } from '../types'; import { IIndexPattern } from '../../../../../../../../src/plugins/data/public'; export interface ServerReturnedEndpointList { @@ -173,11 +173,6 @@ export interface EndpointDetailsActivityLogUpdateIsInvalidDateRange { }; } -export interface EndpointDetailsFlyoutTabChanged { - type: 'endpointDetailsFlyoutTabChanged'; - payload: { flyoutView: EndpointIndexUIQueryParams['show'] }; -} - export type EndpointAction = | ServerReturnedEndpointList | ServerFailedToReturnEndpointList @@ -185,7 +180,6 @@ export type EndpointAction = | ServerFailedToReturnEndpointDetails | EndpointDetailsActivityLogUpdatePaging | EndpointDetailsActivityLogUpdateIsInvalidDateRange - | EndpointDetailsFlyoutTabChanged | EndpointDetailsActivityLogChanged | ServerReturnedEndpointPolicyResponse | ServerFailedToReturnEndpointPolicyResponse diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/builders.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/builders.ts index 384a7b999d826..78da5ec54a44a 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/builders.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/builders.ts @@ -19,7 +19,6 @@ export const initialEndpointPageState = (): Immutable => { loading: false, error: undefined, endpointDetails: { - flyoutView: undefined, activityLog: { paging: { disabled: false, diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/index.test.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/index.test.ts index a9c65c74015c6..1091d9a1a6478 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/index.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/index.test.ts @@ -42,7 +42,6 @@ describe('EndpointList store concerns', () => { loading: false, error: undefined, endpointDetails: { - flyoutView: undefined, activityLog: { paging: { disabled: false, 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 a123f06f62f96..4ef67065a7174 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 @@ -44,7 +44,6 @@ import { } from '../../../../common/lib/endpoint_isolation/mocks'; import { FleetActionGenerator } from '../../../../../common/endpoint/data_generators/fleet_action_generator'; import { endpointPageHttpMock } from '../mocks'; -import { EndpointDetailsTabsTypes } from '../view/details/components/endpoint_details_tabs'; jest.mock('../../policy/store/services/ingest', () => ({ sendGetAgentConfigList: () => Promise.resolve({ items: [] }), @@ -221,20 +220,12 @@ describe('endpoint list middleware', () => { describe('handle ActivityLog State Change actions', () => { const endpointList = getEndpointListApiResponse(); const search = getEndpointDetailsPath({ - name: 'endpointDetails', + name: 'endpointActivityLog', selected_endpoint: endpointList.hosts[0].metadata.agent.id, }); const dispatchUserChangedUrl = () => { dispatchUserChangedUrlToEndpointList({ search: `?${search.split('?').pop()}` }); }; - const dispatchFlyoutViewChange = () => { - dispatch({ - type: 'endpointDetailsFlyoutTabChanged', - payload: { - flyoutView: EndpointDetailsTabsTypes.activityLog, - }, - }); - }; const fleetActionGenerator = new FleetActionGenerator('seed'); const actionData = fleetActionGenerator.generate({ @@ -274,7 +265,6 @@ describe('endpoint list middleware', () => { it('should set ActivityLog state to loading', async () => { dispatchUserChangedUrl(); - dispatchFlyoutViewChange(); const loadingDispatched = waitForAction('endpointDetailsActivityLogChanged', { validate(action) { 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 922f10cee2f8b..32e0fa8258637 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 @@ -34,8 +34,8 @@ import { getActivityLogDataPaging, getLastLoadedActivityLogData, detailsData, - getEndpointDetailsFlyoutView, getIsEndpointPackageInfoUninitialized, + getIsOnEndpointDetailsActivityLog, } from './selectors'; import { AgentIdsPendingActions, EndpointState, PolicyIds } from '../types'; import { @@ -63,7 +63,6 @@ import { AppAction } from '../../../../common/store/actions'; import { resolvePathVariables } from '../../../../common/utils/resolve_path_variables'; import { EndpointPackageInfoStateChanged } from './action'; import { fetchPendingActionsByAgentId } from '../../../../common/lib/endpoint_pending_actions'; -import { EndpointDetailsTabsTypes } from '../view/details/components/endpoint_details_tabs'; import { getIsInvalidDateRange } from '../utils'; type EndpointPageStore = ImmutableMiddlewareAPI; @@ -369,7 +368,7 @@ export const endpointMiddlewareFactory: ImmutableMiddlewareFactory = { location: action.payload, @@ -343,7 +349,7 @@ export const endpointListReducer: StateReducer = (state = initialEndpointPageSta activityLog, hostDetails: { ...state.endpointDetails.hostDetails, - detailsLoading: true, + detailsLoading: !isNotLoadingDetails, detailsError: undefined, }, }, diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts index 4287cf9a109ea..d0126aa3a9489 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts @@ -38,6 +38,7 @@ import { import { ServerApiError } from '../../../../common/types'; import { isEndpointHostIsolated } from '../../../../common/utils/validators'; import { EndpointHostIsolationStatusProps } from '../../../../common/components/endpoint/host_isolation'; +import { EndpointDetailsTabsTypes } from '../view/details/components/endpoint_details_tabs'; export const listData = (state: Immutable) => state.hosts; @@ -362,9 +363,11 @@ export const getIsolationRequestError: ( } }); -export const getEndpointDetailsFlyoutView = ( +export const getIsOnEndpointDetailsActivityLog: ( state: Immutable -): EndpointIndexUIQueryParams['show'] => state.endpointDetails.flyoutView; +) => boolean = createSelector(uiQueryParams, (searchParams) => { + return searchParams.show === EndpointDetailsTabsTypes.activityLog; +}); export const getActivityLogDataPaging = ( state: Immutable diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts index 875841cb55b73..3882ab74f8573 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts @@ -36,7 +36,6 @@ export interface EndpointState { /** api error from retrieving host list */ error?: ServerApiError; endpointDetails: { - flyoutView: EndpointIndexUIQueryParams['show']; activityLog: { paging: { disabled?: boolean; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/endpoint_details_tabs.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/endpoint_details_tabs.tsx index 73a3734e4ca88..a6e571dbd7df9 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/endpoint_details_tabs.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/endpoint_details_tabs.tsx @@ -6,16 +6,18 @@ */ import { useDispatch } from 'react-redux'; -import React, { memo, useCallback, useMemo, useState } from 'react'; +import React, { memo, useCallback, useMemo } from 'react'; import { EuiTab, EuiTabs, EuiFlyoutBody, EuiTabbedContentTab, EuiSpacer } from '@elastic/eui'; import { EndpointIndexUIQueryParams } from '../../../types'; import { EndpointAction } from '../../../store/action'; import { useEndpointSelector } from '../../hooks'; import { getActivityLogDataPaging } from '../../../store/selectors'; import { EndpointDetailsFlyoutHeader } from './flyout_header'; +import { useNavigateByRouterEventHandler } from '../../../../../../common/hooks/endpoint/use_navigate_by_router_event_handler'; +import { useAppUrl } from '../../../../../../common/lib/kibana'; export enum EndpointDetailsTabsTypes { - overview = 'overview', + overview = 'details', activityLog = 'activity_log', } @@ -27,34 +29,52 @@ interface EndpointDetailsTabs { id: string; name: string; content: JSX.Element; + route: string; } +const EndpointDetailsTab = memo( + ({ + tab, + isSelected, + handleTabClick, + }: { + tab: EndpointDetailsTabs; + isSelected: boolean; + handleTabClick: () => void; + }) => { + const { getAppUrl } = useAppUrl(); + const onClick = useNavigateByRouterEventHandler(tab.route, handleTabClick); + return ( + + {tab.name} + + ); + } +); + +EndpointDetailsTab.displayName = 'EndpointDetailsTab'; + export const EndpointDetailsFlyoutTabs = memo( ({ hostname, show, tabs, }: { - hostname?: string; + hostname: string; show: EndpointIndexUIQueryParams['show']; tabs: EndpointDetailsTabs[]; }) => { const dispatch = useDispatch<(action: EndpointAction) => void>(); const { pageSize } = useEndpointSelector(getActivityLogDataPaging); - const [selectedTabId, setSelectedTabId] = useState(() => { - return show === 'details' - ? EndpointDetailsTabsTypes.overview - : EndpointDetailsTabsTypes.activityLog; - }); const handleTabClick = useCallback( (tab: EuiTabbedContentTab) => { - dispatch({ - type: 'endpointDetailsFlyoutTabChanged', - payload: { - flyoutView: tab.id as EndpointIndexUIQueryParams['show'], - }, - }); if (tab.id === EndpointDetailsTabsTypes.activityLog) { dispatch({ type: 'endpointDetailsActivityLogUpdatePaging', @@ -67,25 +87,18 @@ export const EndpointDetailsFlyoutTabs = memo( }, }); } - return setSelectedTabId(tab.id as EndpointDetailsTabsId); }, - [dispatch, pageSize, setSelectedTabId] + [dispatch, pageSize] ); - const selectedTab = useMemo(() => tabs.find((tab) => tab.id === selectedTabId), [ - tabs, - selectedTabId, - ]); + const selectedTab = useMemo(() => tabs.find((tab) => tab.id === show), [tabs, show]); const renderTabs = tabs.map((tab) => ( - handleTabClick(tab)} - isSelected={tab.id === selectedTabId} - key={tab.id} - data-test-subj={tab.id} - > - {tab.name} - + handleTabClick(tab)} + isSelected={tab.id === selectedTab?.id} + /> )); return ( diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/endpoint_isolate_flyout_panel.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/endpoint_isolate_flyout_panel.tsx index 289c1efeab041..527189a3ef394 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/endpoint_isolate_flyout_panel.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/endpoint_isolate_flyout_panel.tsx @@ -9,8 +9,8 @@ import React, { memo, useCallback, useState } from 'react'; import { useHistory } from 'react-router-dom'; import { useDispatch } from 'react-redux'; import { Dispatch } from 'redux'; -import { i18n } from '@kbn/i18n'; import { EuiForm } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { HostMetadata } from '../../../../../../../common/endpoint/types'; import { BackToEndpointDetailsFlyoutSubHeader } from './back_to_endpoint_details_flyout_subheader'; import { @@ -18,6 +18,7 @@ import { EndpointIsolateForm, EndpointIsolateSuccess, EndpointUnisolateForm, + ActionCompletionReturnButton, } from '../../../../../../common/components/endpoint/host_isolation'; import { FlyoutBodyNoTopPadding } from './flyout_body_no_top_padding'; import { getEndpointDetailsPath } from '../../../../../common/routing'; @@ -88,16 +89,20 @@ export const EndpointIsolationFlyoutPanel = memo<{ <> + {wasSuccessful && ( + + )} {wasSuccessful ? ( - ) : ( ( {'Endpoint Details'}, + route: + '/administration/endpoints?page_index=0&page_size=10&selected_endpoint=endpoint-id-00001010&show=details', }, { id: 'activity_log', name: 'Activity Log', content: ActivityLogMarkup(), + route: + '/administration/endpoints?page_index=0&page_size=10&selected_endpoint=endpoint-id-00001010&show=activity_log', }, ]} /> diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/index.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/index.tsx index edfa410ee5237..d3fc812f6317b 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/index.tsx @@ -5,7 +5,6 @@ * 2.0. */ -import { useDispatch } from 'react-redux'; import React, { useCallback, useEffect, useMemo, memo } from 'react'; import { EuiFlyout, @@ -51,14 +50,11 @@ import { PreferenceFormattedDateFromPrimitive } from '../../../../../common/comp import { EndpointIsolationFlyoutPanel } from './components/endpoint_isolate_flyout_panel'; import { BackToEndpointDetailsFlyoutSubHeader } from './components/back_to_endpoint_details_flyout_subheader'; import { FlyoutBodyNoTopPadding } from './components/flyout_body_no_top_padding'; -import { getEndpointListPath } from '../../../../common/routing'; +import { getEndpointListPath, getEndpointDetailsPath } from '../../../../common/routing'; import { ActionsMenu } from './components/actions_menu'; -import { EndpointIndexUIQueryParams } from '../../types'; -import { EndpointAction } from '../../store/action'; import { EndpointDetailsFlyoutHeader } from './components/flyout_header'; export const EndpointDetailsFlyout = memo(() => { - const dispatch = useDispatch<(action: EndpointAction) => void>(); const history = useHistory(); const toasts = useToasts(); const queryParams = useEndpointSelector(uiQueryParams); @@ -75,18 +71,6 @@ export const EndpointDetailsFlyout = memo(() => { const hostStatus = useEndpointSelector(hostStatusInfo); const show = useEndpointSelector(showView); - const setFlyoutView = useCallback( - (flyoutView: EndpointIndexUIQueryParams['show']) => { - dispatch({ - type: 'endpointDetailsFlyoutTabChanged', - payload: { - flyoutView, - }, - }); - }, - [dispatch] - ); - const ContentLoadingMarkup = useMemo( () => ( <> @@ -98,23 +82,40 @@ export const EndpointDetailsFlyout = memo(() => { [] ); - const tabs = [ - { - id: EndpointDetailsTabsTypes.overview, - name: i18.OVERVIEW, - content: - hostDetails === undefined ? ( - ContentLoadingMarkup - ) : ( - - ), - }, - { - id: EndpointDetailsTabsTypes.activityLog, - name: i18.ACTIVITY_LOG.tabTitle, - content: , - }, - ]; + const getTabs = useCallback( + (id: string) => [ + { + id: EndpointDetailsTabsTypes.overview, + name: i18.OVERVIEW, + route: getEndpointDetailsPath({ + ...queryParams, + name: 'endpointDetails', + selected_endpoint: id, + }), + content: + hostDetails === undefined ? ( + ContentLoadingMarkup + ) : ( + + ), + }, + { + id: EndpointDetailsTabsTypes.activityLog, + name: i18.ACTIVITY_LOG.tabTitle, + route: getEndpointDetailsPath({ + ...queryParams, + name: 'endpointActivityLog', + selected_endpoint: id, + }), + content: , + }, + ], + [ContentLoadingMarkup, hostDetails, policyInfo, hostStatus, activityLog, queryParams] + ); const showFlyoutFooter = show === 'details' || show === 'policy_response' || show === 'activity_log'; @@ -127,11 +128,9 @@ export const EndpointDetailsFlyout = memo(() => { ...urlSearchParams, }) ); - setFlyoutView(undefined); - }, [setFlyoutView, history, queryParamsWithoutSelectedEndpoint]); + }, [history, queryParamsWithoutSelectedEndpoint]); useEffect(() => { - setFlyoutView(show); if (hostDetailsError !== undefined) { toasts.addDanger({ title: i18n.translate('xpack.securitySolution.endpoint.details.errorTitle', { @@ -142,10 +141,7 @@ export const EndpointDetailsFlyout = memo(() => { }), }); } - return () => { - setFlyoutView(undefined); - }; - }, [hostDetailsError, setFlyoutView, show, toasts]); + }, [hostDetailsError, show, toasts]); return ( { <> {(show === 'details' || show === 'activity_log') && ( )} 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 26d0d53e39982..48077471b131c 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 @@ -997,7 +997,7 @@ describe('when on the endpoint list page', () => { const subHeaderBackLink = await renderResult.findByTestId('flyoutSubHeaderBackButton'); expect(subHeaderBackLink.textContent).toBe('Endpoint Details'); expect(subHeaderBackLink.getAttribute('href')).toEqual( - `${APP_PATH}${MANAGEMENT_PATH}/endpoints?page_index=0&page_size=10&selected_endpoint=1` + `${APP_PATH}${MANAGEMENT_PATH}/endpoints?page_index=0&page_size=10&selected_endpoint=1&show=details` ); }); @@ -1009,7 +1009,7 @@ describe('when on the endpoint list page', () => { }); const changedUrlAction = await userChangedUrlChecker; expect(changedUrlAction.payload.search).toEqual( - '?page_index=0&page_size=10&selected_endpoint=1' + '?page_index=0&page_size=10&selected_endpoint=1&show=details' ); }); @@ -1082,7 +1082,7 @@ describe('when on the endpoint list page', () => { expect((await changeUrlAction).payload).toMatchObject({ pathname: `${MANAGEMENT_PATH}/endpoints`, - search: '?page_index=0&page_size=10&selected_endpoint=1', + search: '?page_index=0&page_size=10&selected_endpoint=1&show=details', }); }); @@ -1095,7 +1095,7 @@ describe('when on the endpoint list page', () => { expect((await changeUrlAction).payload).toMatchObject({ pathname: `${MANAGEMENT_PATH}/endpoints`, - search: '?page_index=0&page_size=10&selected_endpoint=1', + search: '?page_index=0&page_size=10&selected_endpoint=1&show=details', }); }); @@ -1115,7 +1115,7 @@ describe('when on the endpoint list page', () => { expect((await changeUrlAction).payload).toMatchObject({ pathname: `${MANAGEMENT_PATH}/endpoints`, - search: '?page_index=0&page_size=10&selected_endpoint=1', + search: '?page_index=0&page_size=10&selected_endpoint=1&show=details', }); }); diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/__mocks__/mock.ts b/x-pack/plugins/security_solution/public/network/components/embeddables/__mocks__/mock.ts index ba9b6518c6acc..834447b21929f 100644 --- a/x-pack/plugins/security_solution/public/network/components/embeddables/__mocks__/mock.ts +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/__mocks__/mock.ts @@ -14,6 +14,7 @@ export const mockIndexPatternIds: IndexPatternMapping[] = [ export const mockAPMIndexPatternIds: IndexPatternMapping[] = [ { title: 'apm-*', id: '8c7323ac-97ad-4b53-ac0a-40f8f691a918' }, + { title: 'traces-apm*,logs-apm*,metrics-apm*,apm-*', id: '8c7323ac-97ad-4b53-ac0a-40f8f691a918' }, ]; export const mockSourceLayer = { @@ -183,6 +184,11 @@ export const mockClientLayer = { joins: [], }; +const mockApmDataStreamClientLayer = { + ...mockClientLayer, + label: 'traces-apm*,logs-apm*,metrics-apm*,apm-* | Client Point', +}; + export const mockServerLayer = { sourceDescriptor: { id: 'uuid.v4()', @@ -238,6 +244,11 @@ export const mockServerLayer = { query: { query: '', language: 'kuery' }, }; +const mockApmDataStreamServerLayer = { + ...mockServerLayer, + label: 'traces-apm*,logs-apm*,metrics-apm*,apm-* | Server Point', +}; + export const mockLineLayer = { sourceDescriptor: { type: 'ES_PEW_PEW', @@ -365,6 +376,10 @@ export const mockClientServerLineLayer = { type: 'VECTOR', query: { query: '', language: 'kuery' }, }; +const mockApmDataStreamClientServerLineLayer = { + ...mockClientServerLineLayer, + label: 'traces-apm*,logs-apm*,metrics-apm*,apm-* | Line', +}; export const mockLayerList = [ { @@ -421,6 +436,9 @@ export const mockLayerListMixed = [ mockClientServerLineLayer, mockServerLayer, mockClientLayer, + mockApmDataStreamClientServerLineLayer, + mockApmDataStreamServerLayer, + mockApmDataStreamClientLayer, ]; export const mockAPMIndexPattern: IndexPatternSavedObject = { @@ -468,6 +486,15 @@ export const mockAPMTransactionIndexPattern: IndexPatternSavedObject = { }, }; +export const mockAPMTracesDataStreamIndexPattern: IndexPatternSavedObject = { + id: 'traces-apm*', + type: 'index-pattern', + _version: 'abc', + attributes: { + title: 'traces-apm*', + }, +}; + export const mockGlobIndexPattern: IndexPatternSavedObject = { id: '*', type: 'index-pattern', diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map_helpers.test.tsx b/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map_helpers.test.tsx index 6136f5da51d51..613a6ce4c00da 100644 --- a/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map_helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map_helpers.test.tsx @@ -12,6 +12,7 @@ import { mockAPMIndexPattern, mockAPMRegexIndexPattern, mockAPMTransactionIndexPattern, + mockAPMTracesDataStreamIndexPattern, mockAuditbeatIndexPattern, mockCCSGlobIndexPattern, mockCommaFilebeatAuditbeatCCSGlobIndexPattern, @@ -69,6 +70,7 @@ describe('embedded_map_helpers', () => { describe('findMatchingIndexPatterns', () => { const siemDefaultIndices = [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -102,11 +104,16 @@ describe('embedded_map_helpers', () => { test('finds exact glob-matched index patterns ', () => { const matchingIndexPatterns = findMatchingIndexPatterns({ - kibanaIndexPatterns: [mockAPMTransactionIndexPattern, mockFilebeatIndexPattern], + kibanaIndexPatterns: [ + mockAPMTransactionIndexPattern, + mockAPMTracesDataStreamIndexPattern, + mockFilebeatIndexPattern, + ], siemDefaultIndices, }); expect(matchingIndexPatterns).toEqual([ mockAPMTransactionIndexPattern, + mockAPMTracesDataStreamIndexPattern, mockFilebeatIndexPattern, ]); }); diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/map_config.ts b/x-pack/plugins/security_solution/public/network/components/embeddables/map_config.ts index f4af4dd3b25f2..ecbb80123e07e 100644 --- a/x-pack/plugins/security_solution/public/network/components/embeddables/map_config.ts +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/map_config.ts @@ -61,6 +61,21 @@ export const SUM_OF_DESTINATION_BYTES = 'sum_of_destination.bytes'; export const SUM_OF_CLIENT_BYTES = 'sum_of_client.bytes'; export const SUM_OF_SERVER_BYTES = 'sum_of_server.bytes'; +const APM_LAYER_FIELD_MAPPING = { + source: { + metricField: 'client.bytes', + geoField: 'client.geo.location', + tooltipProperties: Object.keys(clientFieldMappings), + label: i18n.CLIENT_LAYER, + }, + destination: { + metricField: 'server.bytes', + geoField: 'server.geo.location', + tooltipProperties: Object.keys(serverFieldMappings), + label: i18n.SERVER_LAYER, + }, +}; + // Mapping to fields for creating specific layers for a given index pattern // e.g. The apm-* index pattern needs layers for client/server instead of source/destination export const lmc: LayerMappingCollection = { @@ -78,20 +93,8 @@ export const lmc: LayerMappingCollection = { label: i18n.DESTINATION_LAYER, }, }, - 'apm-*': { - source: { - metricField: 'client.bytes', - geoField: 'client.geo.location', - tooltipProperties: Object.keys(clientFieldMappings), - label: i18n.CLIENT_LAYER, - }, - destination: { - metricField: 'server.bytes', - geoField: 'server.geo.location', - tooltipProperties: Object.keys(serverFieldMappings), - label: i18n.SERVER_LAYER, - }, - }, + 'apm-*': APM_LAYER_FIELD_MAPPING, + 'traces-apm*,logs-apm*,metrics-apm*,apm-*': APM_LAYER_FIELD_MAPPING, }; /** diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/pane/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/pane/index.tsx index 3c9d9161a7a48..2afb2af01406d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/pane/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/pane/index.tsx @@ -26,7 +26,7 @@ interface FlyoutPaneComponentProps { const StyledEuiFlyout = styled(EuiFlyout)` animation: none; min-width: 150px; - z-index: ${({ theme }) => theme.eui.euiZLevel6}; + z-index: ${({ theme }) => theme.eui.euiZLevel4}; `; const FlyoutPaneComponent: React.FC = ({ diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx index 94ef690a1fdc1..96b6aa4ebeb8d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx @@ -25,6 +25,7 @@ import { ExpandableEvent, ExpandableEventTitle } from './expandable_event'; import { useTimelineEventsDetails } from '../../../containers/details'; import { TimelineTabs } from '../../../../../common/types/timeline'; import { HostIsolationPanel } from '../../../../detections/components/host_isolation'; +import { EndpointIsolateSuccess } from '../../../../common/components/endpoint/host_isolation'; import { TakeActionDropdown } from '../../../../detections/components/host_isolation/take_action_dropdown'; import { ISOLATE_HOST, @@ -78,10 +79,17 @@ const EventDetailsPanelComponent: React.FC = ({ const [isHostIsolationPanelOpen, setIsHostIsolationPanel] = useState(false); - const [isolateAction, setIsolateAction] = useState('isolateHost'); + const [isolateAction, setIsolateAction] = useState<'isolateHost' | 'unisolateHost'>( + 'isolateHost' + ); + + const [isIsolateActionSuccessBannerVisible, setIsIsolateActionSuccessBannerVisible] = useState( + false + ); const showAlertDetails = useCallback(() => { setIsHostIsolationPanel(false); + setIsIsolateActionSuccessBannerVisible(false); }, []); const { isAllowed: isIsolationAllowed } = useIsolationPrivileges(); @@ -114,6 +122,16 @@ const EventDetailsPanelComponent: React.FC = ({ return findAgentVersion ? findAgentVersion[0] : ''; }, [detailsData]); + const alertId = useMemo(() => { + const findAlertId = find({ category: '_id', field: '_id' }, detailsData)?.values; + return findAlertId ? findAlertId[0] : ''; + }, [detailsData]); + + const hostName = useMemo(() => { + const findHostName = find({ category: 'host', field: 'host.name' }, detailsData)?.values; + return findHostName ? findHostName[0] : ''; + }, [detailsData]); + const isolationSupported = isIsolationSupported({ osName: hostOsFamily, version: agentVersion, @@ -142,6 +160,7 @@ const EventDetailsPanelComponent: React.FC = ({ const caseDetailsRefresh = useWithCaseDetailsRefresh(); const handleIsolationActionSuccess = useCallback(() => { + setIsIsolateActionSuccessBannerVisible(true); // If a case details refresh ref is defined, then refresh actions and comments if (caseDetailsRefresh) { caseDetailsRefresh.refreshUserActionsAndComments(); @@ -161,6 +180,13 @@ const EventDetailsPanelComponent: React.FC = ({ )} + {isIsolateActionSuccessBannerVisible && ( + + )} {isHostIsolationPanelOpen ? ( { wrapper.find('[data-test-subj="timelineSizeRowPopover"] button').first().simulate('click'); expect(wrapper.find('[data-test-subj="timelinePickSizeRow"]').exists()).toBeTruthy(); }); + + test('it renders last updated when updated at is > 0', () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="fixed-width-last-updated"]').exists()).toBeTruthy(); + }); + + test('it does NOT render last updated when updated at is 0', () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="fixed-width-last-updated"]').exists()).toBeFalsy(); + }); }); describe('Events', () => { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.tsx index b71cbb4c082ef..2a253087567a7 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.tsx @@ -45,11 +45,11 @@ const FixedWidthLastUpdatedContainer = React.memo isCompactFooter(width), [width]); - return ( + return updatedAt > 0 ? ( {timelines.getLastUpdated({ updatedAt, compact })} - ); + ) : null; } ); diff --git a/x-pack/plugins/security_solution/server/features.ts b/x-pack/plugins/security_solution/server/features.ts new file mode 100644 index 0000000000000..1756f4cef63e5 --- /dev/null +++ b/x-pack/plugins/security_solution/server/features.ts @@ -0,0 +1,161 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +import { KibanaFeatureConfig, SubFeatureConfig } from '../../features/common'; +import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/server'; +import { APP_ID, SERVER_APP_ID } from '../common/constants'; +import { savedObjectTypes } from './saved_objects'; + +const CASES_SUB_FEATURE: SubFeatureConfig = { + name: 'Cases', + privilegeGroups: [ + { + groupType: 'mutually_exclusive', + privileges: [ + { + id: 'cases_all', + includeIn: 'all', + name: 'All', + savedObject: { + all: [], + read: [], + }, + // using variables with underscores here otherwise when we retrieve them from the kibana + // capabilities in a hook I get type errors regarding boolean | ReadOnly<{[x: string]: boolean}> + ui: ['crud_cases', 'read_cases'], // uiCapabilities.siem.crud_cases + cases: { + all: [APP_ID], + }, + }, + { + id: 'cases_read', + includeIn: 'read', + name: 'Read', + savedObject: { + all: [], + read: [], + }, + // using variables with underscores here otherwise when we retrieve them from the kibana + // capabilities in a hook I get type errors regarding boolean | ReadOnly<{[x: string]: boolean}> + ui: ['read_cases'], // uiCapabilities.siem.read_cases + cases: { + read: [APP_ID], + }, + }, + ], + }, + ], +}; + +export const getAlertsSubFeature = (ruleTypes: string[]): SubFeatureConfig => ({ + name: i18n.translate('xpack.securitySolution.featureRegistry.manageAlertsName', { + defaultMessage: 'Alerts', + }), + privilegeGroups: [ + { + groupType: 'mutually_exclusive', + privileges: [ + { + id: 'alerts_all', + name: i18n.translate('xpack.securitySolution.featureRegistry.subfeature.alertsAllName', { + defaultMessage: 'All', + }), + includeIn: 'all' as 'all', + alerting: { + alert: { + all: ruleTypes, + }, + }, + savedObject: { + all: [], + read: [], + }, + ui: [], + }, + { + id: 'alerts_read', + name: i18n.translate('xpack.securitySolution.featureRegistry.subfeature.alertsReadName', { + defaultMessage: 'Read', + }), + includeIn: 'read' as 'read', + alerting: { + alert: { + read: ruleTypes, + }, + }, + savedObject: { + all: [], + read: [], + }, + ui: [], + }, + ], + }, + ], +}); + +export const getKibanaPrivilegesFeaturePrivileges = ( + ruleTypes: string[], + isRuleRegistryEnabled: boolean +): KibanaFeatureConfig => ({ + id: SERVER_APP_ID, + name: i18n.translate('xpack.securitySolution.featureRegistry.linkSecuritySolutionTitle', { + defaultMessage: 'Security', + }), + order: 1100, + category: DEFAULT_APP_CATEGORIES.security, + app: [APP_ID, 'kibana'], + catalogue: ['securitySolution'], + management: { + insightsAndAlerting: ['triggersActions'], + }, + alerting: ruleTypes, + cases: [APP_ID], + subFeatures: isRuleRegistryEnabled + ? [{ ...CASES_SUB_FEATURE }, { ...getAlertsSubFeature(ruleTypes) }] + : [{ ...CASES_SUB_FEATURE }], + privileges: { + all: { + app: [APP_ID, 'kibana'], + catalogue: ['securitySolution'], + api: ['securitySolution', 'lists-all', 'lists-read', 'rac'], + savedObject: { + all: ['alert', 'exception-list', 'exception-list-agnostic', ...savedObjectTypes], + read: [], + }, + alerting: { + rule: { + all: ruleTypes, + }, + }, + management: { + insightsAndAlerting: ['triggersActions'], + }, + ui: ['show', 'crud'], + }, + read: { + app: [APP_ID, 'kibana'], + catalogue: ['securitySolution'], + api: ['securitySolution', 'lists-read', 'rac'], + savedObject: { + all: [], + read: ['exception-list', 'exception-list-agnostic', ...savedObjectTypes], + }, + alerting: { + rule: { + read: ruleTypes, + }, + }, + management: { + insightsAndAlerting: ['triggersActions'], + }, + ui: ['show'], + }, + }, +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users/detections_admin/detections_role.json b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users/detections_admin/detections_role.json index 6c9b4e2cba49c..82ef8cc6687b4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users/detections_admin/detections_role.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users/detections_admin/detections_role.json @@ -8,6 +8,7 @@ ".lists*", ".items*", "apm-*-transaction*", + "traces-apm*", "auditbeat-*", "endgame-*", "filebeat-*", diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users/hunter/detections_role.json b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users/hunter/detections_role.json index 119fe5421c86c..ba9adfda82bea 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users/hunter/detections_role.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users/hunter/detections_role.json @@ -5,6 +5,7 @@ { "names": [ "apm-*-transaction*", + "traces-apm*", "auditbeat-*", "endgame-*", "filebeat-*", diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users/platform_engineer/detections_role.json b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users/platform_engineer/detections_role.json index 17dbd90d17925..73a9559389b4e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users/platform_engineer/detections_role.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users/platform_engineer/detections_role.json @@ -9,6 +9,7 @@ { "names": [ "apm-*-transaction*", + "traces-apm*", "auditbeat-*", "endgame-*", "filebeat-*", diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users/rule_author/detections_role.json b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users/rule_author/detections_role.json index 0db8359c57764..bb606616d1bd5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users/rule_author/detections_role.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users/rule_author/detections_role.json @@ -5,6 +5,7 @@ { "names": [ "apm-*-transaction*", + "traces-apm*", "auditbeat-*", "endgame-*", "filebeat-*", diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users/soc_manager/detections_role.json b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users/soc_manager/detections_role.json index 6962701ae5be3..92a62034afcef 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users/soc_manager/detections_role.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users/soc_manager/detections_role.json @@ -5,6 +5,7 @@ { "names": [ "apm-*-transaction*", + "traces-apm*", "auditbeat-*", "endgame-*", "filebeat-*", diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users/t1_analyst/detections_role.json b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users/t1_analyst/detections_role.json index 07827069dbc73..be082e380211a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users/t1_analyst/detections_role.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users/t1_analyst/detections_role.json @@ -6,6 +6,7 @@ { "names": [ "apm-*-transaction*", + "traces-apm*", "auditbeat-*", "endgame-*", "filebeat-*", diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users/t2_analyst/detections_role.json b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users/t2_analyst/detections_role.json index f554c916c6684..f9e069f174a91 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users/t2_analyst/detections_role.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users/t2_analyst/detections_role.json @@ -8,6 +8,7 @@ ".lists*", ".items*", "apm-*-transaction*", + "traces-apm*", "auditbeat-*", "endgame-*", "filebeat-*", diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/rules/patches/simplest_updated_name.json b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/rules/patches/simplest_updated_name.json index bec88bcb0e30e..3e5a5c274f1ba 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/rules/patches/simplest_updated_name.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/rules/patches/simplest_updated_name.json @@ -8,6 +8,7 @@ "from": "now-360s", "index": [ "apm-*-transaction*", + "traces-apm*", "auditbeat-*", "endgame-*", "filebeat-*", diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/rules/queries/query_with_mappings.json b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/rules/queries/query_with_mappings.json index f0d7cb4ec914b..2508c9a6a3e37 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/rules/queries/query_with_mappings.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/rules/queries/query_with_mappings.json @@ -3,6 +3,7 @@ "enabled": false, "index": [ "apm-*-transaction*", + "traces-apm*", "auditbeat-*", "endgame-*", "filebeat-*", diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/rules/test_cases/queries/action_without_meta.json b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/rules/test_cases/queries/action_without_meta.json index 6569a641de3a2..f0ddced46d52e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/rules/test_cases/queries/action_without_meta.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/rules/test_cases/queries/action_without_meta.json @@ -2,6 +2,7 @@ "type": "query", "index": [ "apm-*-transaction*", + "traces-apm*", "auditbeat-*", "endgame-*", "filebeat-*", diff --git a/x-pack/plugins/security_solution/server/lib/source_status/elasticsearch_adapter.ts b/x-pack/plugins/security_solution/server/lib/source_status/elasticsearch_adapter.ts index b2058a91c0413..3da0c1675e81e 100644 --- a/x-pack/plugins/security_solution/server/lib/source_status/elasticsearch_adapter.ts +++ b/x-pack/plugins/security_solution/server/lib/source_status/elasticsearch_adapter.ts @@ -12,6 +12,7 @@ import { ApmServiceNameAgg } from './types'; import { ENDPOINT_METADATA_INDEX } from '../../../common/constants'; const APM_INDEX_NAME = 'apm-*-transaction*'; +const APM_DATA_STREAM = 'traces-apm*'; export class ElasticsearchSourceStatusAdapter implements SourceStatusAdapter { constructor(private readonly framework: FrameworkAdapter) {} @@ -23,7 +24,9 @@ export class ElasticsearchSourceStatusAdapter implements SourceStatusAdapter { // Add endpoint metadata index to indices to check indexNames.push(ENDPOINT_METADATA_INDEX); // Remove APM index if exists, and only query if length > 0 in case it's the only index provided - const nonApmIndexNames = indexNames.filter((name) => name !== APM_INDEX_NAME); + const nonApmIndexNames = indexNames.filter( + (name) => name !== APM_INDEX_NAME && name !== APM_DATA_STREAM + ); const indexCheckResponse = await (nonApmIndexNames.length > 0 ? this.framework.callWithRequest(request, 'search', { index: nonApmIndexNames, @@ -39,7 +42,8 @@ export class ElasticsearchSourceStatusAdapter implements SourceStatusAdapter { // Note: Additional check necessary for APM-specific index. For details see: https://github.com/elastic/kibana/issues/56363 // Only verify if APM data exists if indexNames includes `apm-*-transaction*` (default included apm index) - const includesApmIndex = indexNames.includes(APM_INDEX_NAME); + const includesApmIndex = + indexNames.includes(APM_INDEX_NAME) || indexNames.includes(APM_DATA_STREAM); const hasApmDataResponse = await (includesApmIndex ? this.framework.callWithRequest<{}, ApmServiceNameAgg>( request, diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index a8ad6c919a04d..07b0e2ed4b9dd 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -7,7 +7,6 @@ import { once } from 'lodash'; import { Observable } from 'rxjs'; -import { i18n } from '@kbn/i18n'; import LRU from 'lru-cache'; import { @@ -17,7 +16,6 @@ import { Plugin as IPlugin, PluginInitializerContext, SavedObjectsClient, - DEFAULT_APP_CATEGORIES, } from '../../../../src/core/server'; import { PluginSetup as DataPluginSetup, @@ -58,7 +56,7 @@ import { signalRulesAlertType } from './lib/detection_engine/signals/signal_rule import { rulesNotificationAlertType } from './lib/detection_engine/notifications/rules_notification_alert_type'; import { isNotificationAlertExecutor } from './lib/detection_engine/notifications/types'; import { ManifestTask } from './endpoint/lib/artifacts'; -import { initSavedObjects, savedObjectTypes } from './saved_objects'; +import { initSavedObjects } from './saved_objects'; import { AppClientFactory } from './client'; import { createConfig, ConfigType } from './config'; import { initUiSettings } from './ui_settings'; @@ -91,6 +89,7 @@ import { licenseService } from './lib/license'; import { PolicyWatcher } from './endpoint/lib/policy/license_watch'; import { parseExperimentalConfigValue } from '../common/experimental_features'; import { migrateArtifactsToFleet } from './endpoint/lib/artifacts/migrate_artifacts_to_fleet'; +import { getKibanaPrivilegesFeaturePrivileges } from './features'; export interface SetupPlugins { alerting: AlertingSetup; @@ -279,107 +278,9 @@ export class Plugin implements IPlugin - ui: ['crud_cases', 'read_cases'], // uiCapabilities.siem.crud_cases - cases: { - all: [APP_ID], - }, - }, - { - id: 'cases_read', - includeIn: 'read', - name: 'Read', - savedObject: { - all: [], - read: [], - }, - // using variables with underscores here otherwise when we retrieve them from the kibana - // capabilities in a hook I get type errors regarding boolean | ReadOnly<{[x: string]: boolean}> - ui: ['read_cases'], // uiCapabilities.siem.read_cases - cases: { - read: [APP_ID], - }, - }, - ], - }, - ], - }, - ], - privileges: { - all: { - app: [APP_ID, 'kibana'], - catalogue: ['securitySolution'], - api: ['securitySolution', 'lists-all', 'lists-read', 'rac'], - savedObject: { - all: ['alert', 'exception-list', 'exception-list-agnostic', ...savedObjectTypes], - read: [], - }, - alerting: { - rule: { - all: ruleTypes, - }, - alert: { - all: ruleTypes, - }, - }, - management: { - insightsAndAlerting: ['triggersActions'], - }, - ui: ['show', 'crud'], - }, - read: { - app: [APP_ID, 'kibana'], - catalogue: ['securitySolution'], - api: ['securitySolution', 'lists-read', 'rac'], - savedObject: { - all: [], - read: ['exception-list', 'exception-list-agnostic', ...savedObjectTypes], - }, - alerting: { - rule: { - read: ruleTypes, - }, - alert: { - read: ruleTypes, - }, - }, - management: { - insightsAndAlerting: ['triggersActions'], - }, - ui: ['show'], - }, - }, - }); + plugins.features.registerKibanaFeature( + getKibanaPrivilegesFeaturePrivileges(ruleTypes, isRuleRegistryEnabled) + ); // Continue to register legacy rules against alerting client exposed through rule-registry if (this.setupPlugins.alerting != null) { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/__mocks__/index.ts index b6a5435a0e046..0369f182a4c75 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/__mocks__/index.ts @@ -18,6 +18,7 @@ import { export const mockOptions: HostsRequestOptions = { defaultIndex: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -613,6 +614,7 @@ export const formattedSearchStrategyResponse = { allowNoIndices: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -822,6 +824,7 @@ export const expectedDsl = { ignoreUnavailable: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/authentications/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/authentications/__mocks__/index.ts index f29bb58da2f79..1dd3dc8ee4cff 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/authentications/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/authentications/__mocks__/index.ts @@ -17,6 +17,7 @@ import { export const mockOptions: HostAuthenticationsRequestOptions = { defaultIndex: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -2151,6 +2152,7 @@ export const formattedSearchStrategyResponse = { allowNoIndices: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -2372,6 +2374,7 @@ export const expectedDsl = { allowNoIndices: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/__mocks__/index.ts index 9dfff5e11715d..cc97a5f0cacef 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/__mocks__/index.ts @@ -17,6 +17,7 @@ import { export const mockOptions: HostDetailsRequestOptions = { defaultIndex: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -1303,6 +1304,7 @@ export const formattedSearchStrategyResponse = { allowNoIndices: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -1416,6 +1418,7 @@ export const expectedDsl = { allowNoIndices: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/last_first_seen/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/last_first_seen/__mocks__/index.ts index b492bf57f94a6..443e7e96a3c7f 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/last_first_seen/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/last_first_seen/__mocks__/index.ts @@ -14,6 +14,7 @@ import { export const mockOptions: HostFirstLastSeenRequestOptions = { defaultIndex: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -126,6 +127,7 @@ export const formattedSearchStrategyFirstResponse = { allowNoIndices: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -191,6 +193,7 @@ export const formattedSearchStrategyLastResponse = { allowNoIndices: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -225,6 +228,7 @@ export const expectedDsl = { allowNoIndices: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', 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 987754420430d..2b4e4b8291401 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 @@ -15,6 +15,7 @@ import { export const mockOptions: HostOverviewRequestOptions = { defaultIndex: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -119,6 +120,7 @@ export const formattedSearchStrategyResponse = { allowNoIndices: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -331,6 +333,7 @@ export const expectedDsl = { allowNoIndices: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/__mocks__/index.ts index 258e72a5e9b8e..0ad976a0f498c 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/__mocks__/index.ts @@ -10,6 +10,7 @@ import { HostsQueries, SortField } from '../../../../../../../common/search_stra export const mockOptions = { defaultIndex: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -4302,6 +4303,7 @@ export const formattedSearchStrategyResponse = { allowNoIndices: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -4436,6 +4438,7 @@ export const expectedDsl = { allowNoIndices: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/__mocks__/index.ts index c33ca75aa26e1..7f36e3551e5be 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/__mocks__/index.ts @@ -33,6 +33,7 @@ export const formattedAlertsSearchStrategyResponse: MatrixHistogramStrategyRespo { index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -166,6 +167,7 @@ export const expectedDsl = { ignoreUnavailable: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -199,6 +201,7 @@ export const formattedAnomaliesSearchStrategyResponse: MatrixHistogramStrategyRe { index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -381,6 +384,7 @@ export const formattedAuthenticationsSearchStrategyResponse: MatrixHistogramStra { index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -947,6 +951,7 @@ export const formattedEventsSearchStrategyResponse: MatrixHistogramStrategyRespo { index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -1925,6 +1930,7 @@ export const formattedDnsSearchStrategyResponse: MatrixHistogramStrategyResponse allowNoIndices: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/__mocks__/index.ts index 86006c3155447..82531f35b09ab 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/__mocks__/index.ts @@ -10,6 +10,7 @@ import { MatrixHistogramType } from '../../../../../../../common/search_strategy export const mockOptions = { defaultIndex: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -27,6 +28,7 @@ export const mockOptions = { export const expectedDsl = { index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/__mocks__/index.ts index 81da78a132084..ab76d54dee11f 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/__mocks__/index.ts @@ -10,6 +10,7 @@ import { MatrixHistogramType } from '../../../../../../../common/search_strategy export const mockOptions = { defaultIndex: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -27,6 +28,7 @@ export const mockOptions = { export const expectedDsl = { index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/__mocks__/index.ts index 5cf667a0085fa..1fd7b85242df6 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/__mocks__/index.ts @@ -10,6 +10,7 @@ import { MatrixHistogramType } from '../../../../../../../common/search_strategy export const mockOptions = { defaultIndex: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -26,6 +27,7 @@ export const mockOptions = { export const expectedDsl = { index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/__mocks__/index.ts index 9b8dfb139d9f4..4d97fba3cb80c 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/__mocks__/index.ts @@ -10,6 +10,7 @@ import { MatrixHistogramType } from '../../../../../../../common/search_strategy export const mockOptions = { defaultIndex: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -28,6 +29,7 @@ export const expectedDsl = { allowNoIndices: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/__mocks__/index.ts index c361db38a6caa..5dab2bcd5cf9d 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/__mocks__/index.ts @@ -14,6 +14,7 @@ import { export const mockOptions: MatrixHistogramRequestOptions = { defaultIndex: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -31,6 +32,7 @@ export const mockOptions: MatrixHistogramRequestOptions = { export const expectedDsl = { index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -85,6 +87,7 @@ export const expectedDsl = { export const expectedThresholdDsl = { index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -141,6 +144,7 @@ export const expectedThresholdDsl = { export const expectedThresholdMissingFieldDsl = { index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -243,6 +247,7 @@ export const expectedThresholdWithCardinalityDsl = { ignoreUnavailable: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -256,6 +261,7 @@ export const expectedThresholdWithCardinalityDsl = { export const expectedThresholdWithGroupFieldsAndCardinalityDsl = { index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -362,6 +368,7 @@ export const expectedThresholdGroupWithCardinalityDsl = { ignoreUnavailable: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -375,6 +382,7 @@ export const expectedThresholdGroupWithCardinalityDsl = { export const expectedIpIncludingMissingDataDsl = { index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -437,6 +445,7 @@ export const expectedIpIncludingMissingDataDsl = { export const expectedIpNotIncludingMissingDataDsl = { index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/details/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/details/__mocks__/index.ts index fb11069f9c834..7f71906bcaa97 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/details/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/details/__mocks__/index.ts @@ -15,6 +15,7 @@ import { export const mockOptions: NetworkDetailsRequestOptions = { defaultIndex: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -306,6 +307,7 @@ export const formattedSearchStrategyResponse = { allowNoIndices: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -447,6 +449,7 @@ export const expectedDsl = { allowNoIndices: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/dns/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/dns/__mocks__/index.ts index 3252a7c249d72..cc01450e5bec5 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/dns/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/dns/__mocks__/index.ts @@ -17,6 +17,7 @@ import { export const mockOptions: NetworkDnsRequestOptions = { defaultIndex: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -133,6 +134,7 @@ export const formattedSearchStrategyResponse = { allowNoIndices: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -204,6 +206,7 @@ export const expectedDsl = { allowNoIndices: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/http/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/http/__mocks__/index.ts index aaf29f07537b5..b34027338e2ba 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/http/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/http/__mocks__/index.ts @@ -18,6 +18,7 @@ import { export const mockOptions: NetworkHttpRequestOptions = { defaultIndex: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -621,6 +622,7 @@ export const formattedSearchStrategyResponse = { allowNoIndices: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -678,6 +680,7 @@ export const expectedDsl = { allowNoIndices: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/overview/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/overview/__mocks__/index.ts index fcb30be7a403d..74b201e9a2294 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/overview/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/overview/__mocks__/index.ts @@ -15,6 +15,7 @@ import { export const mockOptions: NetworkOverviewRequestOptions = { defaultIndex: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -103,6 +104,7 @@ export const formattedSearchStrategyResponse = { allowNoIndices: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -208,6 +210,7 @@ export const expectedDsl = { ignoreUnavailable: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/tls/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/tls/__mocks__/index.ts index 16750acc5adee..8616a2ef14856 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/tls/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/tls/__mocks__/index.ts @@ -18,6 +18,7 @@ import { export const mockOptions: NetworkTlsRequestOptions = { defaultIndex: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -61,6 +62,7 @@ export const formattedSearchStrategyResponse = { allowNoIndices: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -115,6 +117,7 @@ export const expectedDsl = { allowNoIndices: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_countries/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_countries/__mocks__/index.ts index 9f95dbe9c1c4f..ba5db90df8245 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_countries/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_countries/__mocks__/index.ts @@ -18,6 +18,7 @@ import { export const mockOptions: NetworkTopCountriesRequestOptions = { defaultIndex: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -60,6 +61,7 @@ export const formattedSearchStrategyResponse = { allowNoIndices: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -119,6 +121,7 @@ export const expectedDsl = { allowNoIndices: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_n_flow/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_n_flow/__mocks__/index.ts index c815ed22f2b54..e881a9ef93949 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_n_flow/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_n_flow/__mocks__/index.ts @@ -19,6 +19,7 @@ import { export const mockOptions: NetworkTopNFlowRequestOptions = { defaultIndex: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -812,6 +813,7 @@ export const formattedSearchStrategyResponse: NetworkTopNFlowStrategyResponse = allowNoIndices: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -879,6 +881,7 @@ export const expectedDsl = { allowNoIndices: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/users/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/users/__mocks__/index.ts index 3837afabe5799..686730dbe7927 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/users/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/users/__mocks__/index.ts @@ -18,6 +18,7 @@ import { export const mockOptions: NetworkUsersRequestOptions = { defaultIndex: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -121,6 +122,7 @@ export const formattedSearchStrategyResponse = { allowNoIndices: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -210,6 +212,7 @@ export const expectedDsl = { ignoreUnavailable: true, index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', diff --git a/x-pack/plugins/timelines/public/components/t_grid/body/column_headers/__snapshots__/index.test.tsx.snap b/x-pack/plugins/timelines/public/components/t_grid/body/column_headers/__snapshots__/index.test.tsx.snap index 9ee08bcd966f3..e6e56818bcc84 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/body/column_headers/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/timelines/public/components/t_grid/body/column_headers/__snapshots__/index.test.tsx.snap @@ -367,6 +367,7 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = ` "format": "", "indexes": Array [ "apm-*-transaction*", + "traces-apm*", "auditbeat-*", "endgame-*", "filebeat-*", diff --git a/x-pack/plugins/timelines/public/mock/browser_fields.ts b/x-pack/plugins/timelines/public/mock/browser_fields.ts index 1581175e32904..6ab06e1be018a 100644 --- a/x-pack/plugins/timelines/public/mock/browser_fields.ts +++ b/x-pack/plugins/timelines/public/mock/browser_fields.ts @@ -10,6 +10,7 @@ import type { BrowserFields } from '../../common/search_strategy/index_fields'; const DEFAULT_INDEX_PATTERN = [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', diff --git a/x-pack/plugins/timelines/public/mock/global_state.ts b/x-pack/plugins/timelines/public/mock/global_state.ts index bb7bee3d1552a..f7d3297738373 100644 --- a/x-pack/plugins/timelines/public/mock/global_state.ts +++ b/x-pack/plugins/timelines/public/mock/global_state.ts @@ -24,6 +24,7 @@ export const mockGlobalState: TimelineState = { id: 'test', indexNames: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', diff --git a/x-pack/plugins/timelines/server/search_strategy/index_fields/index.test.ts b/x-pack/plugins/timelines/server/search_strategy/index_fields/index.test.ts index f6d78f2f1259f..cb2b097d78701 100644 --- a/x-pack/plugins/timelines/server/search_strategy/index_fields/index.test.ts +++ b/x-pack/plugins/timelines/server/search_strategy/index_fields/index.test.ts @@ -845,7 +845,7 @@ describe('Fields Provider', () => { }); it('should search apm index fields', async () => { - const indices = ['apm-*-transaction*']; + const indices = ['apm-*-transaction*', 'traces-apm*']; const request = { indices, onlyCheckIfIndicesExist: false, @@ -861,13 +861,13 @@ describe('Fields Provider', () => { }); it('should check apm index exists with data', async () => { - const indices = ['apm-*-transaction*']; + const indices = ['apm-*-transaction*', 'traces-apm*']; const request = { indices, onlyCheckIfIndicesExist: true, }; - esClientSearchMock.mockResolvedValueOnce({ + esClientSearchMock.mockResolvedValue({ body: { hits: { total: { value: 1 } } }, }); const response = await requestIndexFieldSearch(request, deps, beatFields); @@ -876,6 +876,10 @@ describe('Fields Provider', () => { index: indices[0], body: { query: { match_all: {} }, size: 0 }, }); + expect(esClientSearchMock).toHaveBeenCalledWith({ + index: indices[1], + body: { query: { match_all: {} }, size: 0 }, + }); expect(getFieldsForWildcardMock).not.toHaveBeenCalled(); expect(response.indexFields).toHaveLength(0); @@ -883,13 +887,13 @@ describe('Fields Provider', () => { }); it('should check apm index exists with no data', async () => { - const indices = ['apm-*-transaction*']; + const indices = ['apm-*-transaction*', 'traces-apm*']; const request = { indices, onlyCheckIfIndicesExist: true, }; - esClientSearchMock.mockResolvedValueOnce({ + esClientSearchMock.mockResolvedValue({ body: { hits: { total: { value: 0 } } }, }); @@ -899,6 +903,10 @@ describe('Fields Provider', () => { index: indices[0], body: { query: { match_all: {} }, size: 0 }, }); + expect(esClientSearchMock).toHaveBeenCalledWith({ + index: indices[1], + body: { query: { match_all: {} }, size: 0 }, + }); expect(getFieldsForWildcardMock).not.toHaveBeenCalled(); expect(response.indexFields).toHaveLength(0); diff --git a/x-pack/plugins/timelines/server/search_strategy/index_fields/index.ts b/x-pack/plugins/timelines/server/search_strategy/index_fields/index.ts index d100e8db21493..b6cf4af1561c3 100644 --- a/x-pack/plugins/timelines/server/search_strategy/index_fields/index.ts +++ b/x-pack/plugins/timelines/server/search_strategy/index_fields/index.ts @@ -25,6 +25,7 @@ import { } from '../../../common/search_strategy/index_fields'; const apmIndexPattern = 'apm-*-transaction*'; +const apmDataStreamsPattern = 'traces-apm*'; export const indexFieldsProvider = (): ISearchStrategy< IndexFieldsStrategyRequest, @@ -51,7 +52,10 @@ export const requestIndexFieldSearch = async ( const responsesIndexFields = await Promise.all( dedupeIndices .map(async (index) => { - if (request.onlyCheckIfIndicesExist && index.includes(apmIndexPattern)) { + if ( + request.onlyCheckIfIndicesExist && + (index.includes(apmIndexPattern) || index.includes(apmDataStreamsPattern)) + ) { // for apm index pattern check also if there's data https://github.com/elastic/kibana/issues/90661 const searchResponse = await esClient.asCurrentUser.search({ index, diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/all/helpers.test.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/all/helpers.test.ts index 9197917ad764f..c9be6582015f1 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/all/helpers.test.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/all/helpers.test.ts @@ -141,7 +141,7 @@ describe('#formatTimelineData', () => { parent: { depth: 0, index: - 'apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,winlogbeat-*', + 'apm-*-transaction*,traces-apm*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,winlogbeat-*', id: '0268af90-d8da-576a-9747-2a191519416a', type: 'event', }, @@ -180,6 +180,7 @@ describe('#formatTimelineData', () => { query: '_id :*', index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -246,7 +247,7 @@ describe('#formatTimelineData', () => { { depth: 0, index: - 'apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,winlogbeat-*', + 'apm-*-transaction*,traces-apm*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,winlogbeat-*', id: '0268af90-d8da-576a-9747-2a191519416a', type: 'event', }, @@ -255,7 +256,7 @@ describe('#formatTimelineData', () => { { depth: 0, index: - 'apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,winlogbeat-*', + 'apm-*-transaction*,traces-apm*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,winlogbeat-*', id: '0268af90-d8da-576a-9747-2a191519416a', type: 'event', }, @@ -279,6 +280,7 @@ describe('#formatTimelineData', () => { 'signal.rule.version': ['1'], 'signal.rule.index': [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', @@ -332,6 +334,7 @@ describe('#formatTimelineData', () => { id: ['696c24e0-526d-11eb-836c-e1620268b945'], index: [ 'apm-*-transaction*', + 'traces-apm*', 'auditbeat-*', 'endgame-*', 'filebeat-*', diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 2b1088d8c11ae..1709ee6b30665 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -262,7 +262,6 @@ "core.euiCodeEditor.stopEditing": "完了したら Esc キーで編集を終了します。", "core.euiCodeEditor.stopInteracting": "完了したら Esc キーでコードの操作を終了します。", "core.euiCollapsedItemActions.allActions": "すべてのアクション", - "core.euiCollapsibleNav.closeButtonLabel": "閉じる", "core.euiColorPicker.alphaLabel": "アルファチャネル (不透明) 値", "core.euiColorPicker.closeLabel": "下矢印キーを押すと、色オプションを含むポップオーバーが開きます", "core.euiColorPicker.colorErrorMessage": "無効な色値", @@ -337,7 +336,6 @@ "core.euiFlyout.closeAriaLabel": "このダイアログを閉じる", "core.euiForm.addressFormErrors": "ハイライトされたエラーを修正してください。", "core.euiFormControlLayoutClearButton.label": "インプットを消去", - "core.euiHeaderAlert.dismiss": "閉じる", "core.euiHeaderLinks.appNavigation": "アプリメニュー", "core.euiHeaderLinks.openNavigationMenu": "メニューを開く", "core.euiHue.label": "HSV カラーモードの「色相」値を選択", @@ -444,19 +442,12 @@ "core.euiSuperUpdateButton.refreshButtonLabel": "更新", "core.euiSuperUpdateButton.updateButtonLabel": "更新", "core.euiSuperUpdateButton.updatingButtonLabel": "更新中", - "core.euiTableHeaderCell.clickForAscending": "クリックすると、昇順で並べ替えます", - "core.euiTableHeaderCell.clickForDescending": "クリックすると、降順で並べ替えます", - "core.euiTableHeaderCell.clickForUnsort": "クリックすると、並べ替えを解除します", - "core.euiTableHeaderCell.titleTextWithSort": "{innerText}; {ariaSortValue}順に表示", "core.euiTablePagination.rowsPerPage": "ページごとの行数", "core.euiTablePagination.rowsPerPageOption": "{rowsPerPage} 行", "core.euiTableSortMobile.sorting": "並べ替え", "core.euiToast.dismissToast": "トーストを閉じる", "core.euiToast.newNotification": "新しい通知が表示されます", "core.euiToast.notification": "通知", - "core.euiTour.closeTour": "ツアーを閉じる", - "core.euiTour.endTour": "ツアーを終了", - "core.euiTour.skipTour": "ツアーをスキップ", "core.euiTourStepIndicator.ariaLabel": "ステップ{number} {status}", "core.euiTourStepIndicator.isActive": "アクティブ", "core.euiTourStepIndicator.isComplete": "完了", @@ -673,6 +664,7 @@ "dashboard.topNav.options.syncColorsBetweenPanelsSwitchLabel": "パネル全体でカラーパレットを同期", "dashboard.topNav.options.useMarginsBetweenPanelsSwitchLabel": "パネルの間に余白を使用", "dashboard.topNav.saveModal.descriptionFormRowLabel": "説明", + "dashboard.topNav.saveModal.objectType": "dashboard", "dashboard.topNav.saveModal.storeTimeWithDashboardFormRowHelpText": "有効化すると、ダッシュボードが読み込まれるごとに現在選択された時刻の時間フィルターが変更されます。", "dashboard.topNav.saveModal.storeTimeWithDashboardFormRowLabel": "ダッシュボードに時刻を保存", "dashboard.topNav.showCloneModal.dashboardCopyTitle": "{title}のコピー", @@ -1698,6 +1690,7 @@ "discover.localMenu.openTitle": "開く", "discover.localMenu.optionsDescription": "オプション", "discover.localMenu.saveSaveSearchDescription": "ビジュアライゼーションとダッシュボードで使用できるように Discover の検索を保存します", + "discover.localMenu.saveSaveSearchObjectType": "discover", "discover.localMenu.saveSearchDescription": "検索を保存します", "discover.localMenu.saveTitle": "保存", "discover.localMenu.shareSearchDescription": "検索を共有します", @@ -5138,6 +5131,7 @@ "visualize.topNavMenu.saveVisualizationButtonAriaLabel": "ビジュアライゼーションを保存", "visualize.topNavMenu.saveVisualizationButtonLabel": "保存", "visualize.topNavMenu.saveVisualizationDisabledButtonTooltip": "保存する前に変更を適用または破棄", + "visualize.topNavMenu.saveVisualizationObjectType": "visualize", "visualize.topNavMenu.saveVisualizationToLibraryButtonLabel": "ライブラリに保存", "visualize.topNavMenu.shareVisualizationButtonAriaLabel": "ビジュアライゼーションを共有", "visualize.topNavMenu.shareVisualizationButtonLabel": "共有", @@ -6100,9 +6094,8 @@ "xpack.canvas.error.useImportWorkpad.missingPropertiesErrorMessage": "{CANVAS} ワークパッドに必要なプロパティの一部が欠けています。 {JSON} ファイルを編集して正しいプロパティ値を入力し、再試行してください。", "xpack.canvas.error.workpadRoutes.createFailureErrorMessage": "ワークパッドを作成できませんでした", "xpack.canvas.error.workpadRoutes.loadFailureErrorMessage": "ID でワークパッドを読み込めませんでした", - "xpack.canvas.errorComponent.description": "表現が失敗し次のメッセージが返されました:", - "xpack.canvas.errorComponent.title": "おっと!表現が失敗しました", - "xpack.canvas.errors.useImportWorkpad.fileUploadFileWithFileNameErrorMessage": "「{fileName}」をアップロードできませんでした", + "expressionError.errorComponent.description": "表現が失敗し次のメッセージが返されました:", + "expressionError.errorComponent.title": "おっと!表現が失敗しました", "xpack.canvas.expression.cancelButtonLabel": "キャンセル", "xpack.canvas.expression.closeButtonLabel": "閉じる", "xpack.canvas.expression.learnLinkText": "表現構文の詳細", @@ -6514,15 +6507,15 @@ "xpack.canvas.renderer.advancedFilter.displayName": "高度なフィルター", "xpack.canvas.renderer.advancedFilter.helpDescription": "Canvas フィルター表現をレンダリングします。", "xpack.canvas.renderer.advancedFilter.inputPlaceholder": "フィルター表現を入力", - "xpack.canvas.renderer.debug.displayName": "デバッグ", - "xpack.canvas.renderer.debug.helpDescription": "デバッグアウトプットをフォーマットされた {JSON} としてレンダリングします", + "expressionError.renderer.debug.displayName": "デバッグ", + "expressionError.renderer.debug.helpDescription": "デバッグアウトプットをフォーマットされた {JSON} としてレンダリングします", "xpack.canvas.renderer.dropdownFilter.displayName": "ドロップダウンフィルター", "xpack.canvas.renderer.dropdownFilter.helpDescription": "「{exactly}」フィルターの値を選択できるドロップダウンです", "xpack.canvas.renderer.dropdownFilter.matchAllOptionLabel": "すべて", "xpack.canvas.renderer.embeddable.displayName": "埋め込み可能", "xpack.canvas.renderer.embeddable.helpDescription": "Kibana の他の部分から埋め込み可能な保存済みオブジェクトをレンダリングします", - "xpack.canvas.renderer.error.displayName": "エラー情報", - "xpack.canvas.renderer.error.helpDescription": "エラーデータをユーザーにわかるようにレンダリングします", + "expressionError.renderer.error.displayName": "エラー情報", + "expressionError.renderer.error.helpDescription": "エラーデータをユーザーにわかるようにレンダリングします", "xpack.canvas.renderer.image.displayName": "画像", "xpack.canvas.renderer.image.helpDescription": "画像をレンダリングします", "xpack.canvas.renderer.markdown.displayName": "マークダウン", @@ -9207,7 +9200,6 @@ "xpack.fleet.setupPage.gettingStartedText": "詳細については、{link}ガイドをお読みください。", "xpack.fleet.setupPage.missingRequirementsCalloutDescription": "Elasticエージェントの集中管理を使用するには、次のElasticsearchのセキュリティ機能を有効にする必要があります。", "xpack.fleet.setupPage.missingRequirementsCalloutTitle": "不足しているセキュリティ要件", - "xpack.fleet.setupPage.missingRequirementsElasticsearchTitle": "Elasticsearchポリシーでは、次のことができます。", "xpack.fleet.unenrollAgents.cancelButtonLabel": "キャンセル", "xpack.fleet.unenrollAgents.confirmMultipleButtonLabel": "{count}個のエージェントを登録解除", "xpack.fleet.unenrollAgents.confirmSingleButtonLabel": "エージェントの登録解除", @@ -17158,7 +17150,6 @@ "xpack.observability.feedbackMenu.appName": "オブザーバビリティ", "xpack.observability.fieldValueSelection.apply": "適用", "xpack.observability.fieldValueSelection.placeholder": "{label}をフィルタリング", - "xpack.observability.fleet.beta": "ベータ", "xpack.observability.fleet.button": "Fleet ベータを試す", "xpack.observability.fleet.text": "Elastic エージェントでは、シンプルかつ統合された方法で、ログ、メトリック、他の種類のデータの監視をホストに追加することができます。複数の Beats と他のエージェントをインストールする必要はありません。このため、インフラストラクチャ全体での構成のデプロイが簡単で高速になりました。", "xpack.observability.fleet.title": "新しい Fleet をご覧になりましたか。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 04394a1ac1704..76d6ad2f903ad 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -265,7 +265,6 @@ "core.euiCodeEditor.stopEditing": "完成后,按 Esc 键停止编辑。", "core.euiCodeEditor.stopInteracting": "完成后,按 Esc 键停止与代码互动。", "core.euiCollapsedItemActions.allActions": "所有操作", - "core.euiCollapsibleNav.closeButtonLabel": "关闭", "core.euiColorPicker.alphaLabel": "Alpha 通道 (不透明度) 值", "core.euiColorPicker.closeLabel": "按向下箭头键可打开包含颜色选项的弹出框", "core.euiColorPicker.colorErrorMessage": "颜色值无效", @@ -340,7 +339,6 @@ "core.euiFlyout.closeAriaLabel": "关闭此对话框", "core.euiForm.addressFormErrors": "请解决突出显示的错误。", "core.euiFormControlLayoutClearButton.label": "清除输入", - "core.euiHeaderAlert.dismiss": "关闭", "core.euiHeaderLinks.appNavigation": "应用菜单", "core.euiHeaderLinks.openNavigationMenu": "打开菜单", "core.euiHue.label": "选择 HSV 颜色模式“色调”值", @@ -447,19 +445,12 @@ "core.euiSuperUpdateButton.refreshButtonLabel": "刷新", "core.euiSuperUpdateButton.updateButtonLabel": "更新", "core.euiSuperUpdateButton.updatingButtonLabel": "正在更新", - "core.euiTableHeaderCell.clickForAscending": "单击升序排序", - "core.euiTableHeaderCell.clickForDescending": "单击降序排序", - "core.euiTableHeaderCell.clickForUnsort": "单击取消排序", - "core.euiTableHeaderCell.titleTextWithSort": "{innerText};已按 {ariaSortValue} 顺序排序", "core.euiTablePagination.rowsPerPage": "每页行数", "core.euiTablePagination.rowsPerPageOption": "{rowsPerPage} 行", "core.euiTableSortMobile.sorting": "排序", "core.euiToast.dismissToast": "关闭 Toast", "core.euiToast.newNotification": "新通知出现", "core.euiToast.notification": "通知", - "core.euiTour.closeTour": "关闭教程", - "core.euiTour.endTour": "结束教程", - "core.euiTour.skipTour": "跳过教程", "core.euiTourStepIndicator.ariaLabel": "第 {number} 步{status}", "core.euiTourStepIndicator.isActive": "活动", "core.euiTourStepIndicator.isComplete": "已完成", @@ -676,6 +667,7 @@ "dashboard.topNav.options.syncColorsBetweenPanelsSwitchLabel": "在面板之间同步调色板", "dashboard.topNav.options.useMarginsBetweenPanelsSwitchLabel": "在面板间使用边距", "dashboard.topNav.saveModal.descriptionFormRowLabel": "描述", + "dashboard.topNav.saveModal.objectType": "dashboard", "dashboard.topNav.saveModal.storeTimeWithDashboardFormRowHelpText": "每次加载此仪表板时,都会将时间筛选更改为当前选定的时间。", "dashboard.topNav.saveModal.storeTimeWithDashboardFormRowLabel": "将时间随仪表板保存", "dashboard.topNav.showCloneModal.dashboardCopyTitle": "{title} 副本", @@ -1708,6 +1700,7 @@ "discover.localMenu.openTitle": "打开", "discover.localMenu.optionsDescription": "选项", "discover.localMenu.saveSaveSearchDescription": "保存您的 Discover 搜索,以便可以在可视化和仪表板中使用该搜索", + "discover.localMenu.saveSaveSearchObjectType": "discover", "discover.localMenu.saveSearchDescription": "保存搜索", "discover.localMenu.saveTitle": "保存", "discover.localMenu.shareSearchDescription": "共享搜索", @@ -5166,6 +5159,7 @@ "visualize.topNavMenu.saveVisualizationButtonAriaLabel": "保存可视化", "visualize.topNavMenu.saveVisualizationButtonLabel": "保存", "visualize.topNavMenu.saveVisualizationDisabledButtonTooltip": "保存前应用或放弃所做更改", + "visualize.topNavMenu.saveVisualizationObjectType": "visualize", "visualize.topNavMenu.saveVisualizationToLibraryButtonLabel": "保存到库", "visualize.topNavMenu.shareVisualizationButtonAriaLabel": "共享可视化", "visualize.topNavMenu.shareVisualizationButtonLabel": "共享", @@ -6139,9 +6133,8 @@ "xpack.canvas.error.useImportWorkpad.missingPropertiesErrorMessage": "{CANVAS} Workpad 所需的某些属性缺失。 编辑 {JSON} 文件以提供正确的属性值,然后重试。", "xpack.canvas.error.workpadRoutes.createFailureErrorMessage": "无法创建 Workpad", "xpack.canvas.error.workpadRoutes.loadFailureErrorMessage": "无法加载具有以下 ID 的 Workpad", - "xpack.canvas.errorComponent.description": "表达式失败,并显示消息:", - "xpack.canvas.errorComponent.title": "哎哟!表达式失败", - "xpack.canvas.errors.useImportWorkpad.fileUploadFileWithFileNameErrorMessage": "无法上传“{fileName}”", + "expressionError.errorComponent.description": "表达式失败,并显示消息:", + "expressionError.errorComponent.title": "哎哟!表达式失败", "xpack.canvas.expression.cancelButtonLabel": "取消", "xpack.canvas.expression.closeButtonLabel": "关闭", "xpack.canvas.expression.learnLinkText": "学习表达式语法", @@ -6554,15 +6547,15 @@ "xpack.canvas.renderer.advancedFilter.displayName": "高级筛选", "xpack.canvas.renderer.advancedFilter.helpDescription": "呈现 Canvas 筛选表达式", "xpack.canvas.renderer.advancedFilter.inputPlaceholder": "输入筛选表达式", - "xpack.canvas.renderer.debug.displayName": "故障排查", - "xpack.canvas.renderer.debug.helpDescription": "将故障排查输出呈现为带格式的 {JSON}", + "expressionError.renderer.debug.displayName": "故障排查", + "expressionError.renderer.debug.helpDescription": "将故障排查输出呈现为带格式的 {JSON}", "xpack.canvas.renderer.dropdownFilter.displayName": "下拉列表筛选", "xpack.canvas.renderer.dropdownFilter.helpDescription": "可以从其中为“{exactly}”筛选选择值的下拉列表", "xpack.canvas.renderer.dropdownFilter.matchAllOptionLabel": "任意", "xpack.canvas.renderer.embeddable.displayName": "可嵌入", "xpack.canvas.renderer.embeddable.helpDescription": "从 Kibana 的其他部分呈现可嵌入的已保存对象", - "xpack.canvas.renderer.error.displayName": "错误信息", - "xpack.canvas.renderer.error.helpDescription": "以用户友好的方式呈现错误数据", + "expressionError.renderer.error.displayName": "错误信息", + "expressionError.renderer.error.helpDescription": "以用户友好的方式呈现错误数据", "xpack.canvas.renderer.image.displayName": "图像", "xpack.canvas.renderer.image.helpDescription": "呈现图像", "xpack.canvas.renderer.markdown.displayName": "Markdown", @@ -9293,7 +9286,6 @@ "xpack.fleet.setupPage.gettingStartedText": "有关更多信息,请阅读我们的{link}指南。", "xpack.fleet.setupPage.missingRequirementsCalloutDescription": "要对 Elastic 代理使用集中管理,请启用下面的 Elasticsearch 安全功能。", "xpack.fleet.setupPage.missingRequirementsCalloutTitle": "缺失安全性要求", - "xpack.fleet.setupPage.missingRequirementsElasticsearchTitle": "在 Elasticsearch 策略中,启用:", "xpack.fleet.unenrollAgents.cancelButtonLabel": "取消", "xpack.fleet.unenrollAgents.confirmMultipleButtonLabel": "取消注册 {count} 个代理", "xpack.fleet.unenrollAgents.confirmSingleButtonLabel": "取消注册代理", @@ -17394,7 +17386,6 @@ "xpack.observability.feedbackMenu.appName": "可观测性", "xpack.observability.fieldValueSelection.apply": "应用", "xpack.observability.fieldValueSelection.placeholder": "筛选 {label}", - "xpack.observability.fleet.beta": "公测版", "xpack.observability.fleet.button": "试用 Fleet 公测版", "xpack.observability.fleet.text": "通过 Elastic 代理,可以简单统一的方式将日志、指标和其他类型数据的监测添加到主机。您无需安装多个 Beats 和其他代理,以令其更为方便快捷地在基础结构中部署配置。", "xpack.observability.fleet.title": "您是否了解我们的全新 Fleet?", diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/steps/swimlane_connection.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/steps/swimlane_connection.tsx index 2bf99ec9d62b6..e81b607f55971 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/steps/swimlane_connection.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/steps/swimlane_connection.tsx @@ -4,7 +4,15 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { EuiCallOut, EuiFieldText, EuiFormRow, EuiLink, EuiSpacer, EuiText } from '@elastic/eui'; +import { + EuiCallOut, + EuiFieldText, + EuiFormRow, + EuiLink, + EuiSpacer, + EuiText, + EuiFieldPassword, +} from '@elastic/eui'; import React, { useCallback } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import * as i18n from '../translations'; @@ -141,7 +149,7 @@ const SwimlaneConnectionComponent: React.FunctionComponent = ({ )} - { services.scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); services.scopedClusterClient.asCurrentUser = (jest.fn() as unknown) as any; @@ -77,13 +81,16 @@ const mockOptions = ( params, services, state, + rule, }; }; describe('status check alert', () => { let toISOStringSpy: jest.SpyInstance; + const mockDate = new Date('2021-05-13T12:33:37.000Z'); beforeEach(() => { toISOStringSpy = jest.spyOn(Date.prototype, 'toISOString'); + Date.now = jest.fn().mockReturnValue(mockDate); }); afterEach(() => { @@ -108,10 +115,14 @@ describe('status check alert', () => { "filters": undefined, "locations": Array [], "numTimes": 5, - "timerange": Object { + "timespanRange": Object { "from": "now-15m", "to": "now", }, + "timestampRange": Object { + "from": 1620821917000, + "to": "now", + }, "uptimeEsClient": Object { "baseESClient": [MockFunction], "count": [Function], @@ -163,10 +174,14 @@ describe('status check alert', () => { "filters": undefined, "locations": Array [], "numTimes": 5, - "timerange": Object { + "timespanRange": Object { "from": "now-15m", "to": "now", }, + "timestampRange": Object { + "from": 1620821917000, + "to": "now", + }, "uptimeEsClient": Object { "baseESClient": [MockFunction], "count": [Function], @@ -476,10 +491,14 @@ describe('status check alert', () => { }, "locations": Array [], "numTimes": 3, - "timerange": Object { + "timespanRange": Object { "from": "now-15m", "to": "now", }, + "timestampRange": Object { + "from": 1620821917000, + "to": "now", + }, "uptimeEsClient": Object { "baseESClient": [MockFunction], "count": [Function], @@ -583,10 +602,14 @@ describe('status check alert', () => { }, "locations": Array [], "numTimes": 20, - "timerange": Object { + "timespanRange": Object { "from": "now-30h", "to": "now", }, + "timestampRange": Object { + "from": 1620714817000, + "to": "now", + }, "uptimeEsClient": Object { "baseESClient": [MockFunction], "count": [Function], @@ -900,6 +923,85 @@ describe('status check alert', () => { }); }); + it('generates timespan and @timestamp ranges appropriately', async () => { + const mockGetter = jest.fn(); + mockGetter.mockReturnValue([]); + const { server, libs, plugins } = bootstrapDependencies({ + getIndexPattern: jest.fn(), + getMonitorStatus: mockGetter, + }); + const alert = statusCheckAlertFactory(server, libs, plugins); + const options = mockOptions({ + numTimes: 20, + timerangeCount: 30, + timerangeUnit: 'h', + filters: { + 'monitor.type': ['http'], + 'observer.geo.name': [], + tags: [], + 'url.port': [], + }, + search: 'url.full: *', + }); + await alert.executor(options); + + expect(mockGetter).toHaveBeenCalledTimes(1); + expect(mockGetter.mock.calls[0][0]).toEqual( + expect.objectContaining({ + timespanRange: { + from: 'now-30h', + to: 'now', + }, + timestampRange: { + from: mockDate.setHours(mockDate.getHours() - 54).valueOf(), // now minus the timerange (30h), plus an additional 24 hour buffer + to: 'now', + }, + }) + ); + }); + + it('uses the larger of alert interval and timerange when defining timestampRange', async () => { + const mockGetter = jest.fn(); + mockGetter.mockReturnValue([]); + const { server, libs, plugins } = bootstrapDependencies({ + getIndexPattern: jest.fn(), + getMonitorStatus: mockGetter, + }); + const alert = statusCheckAlertFactory(server, libs, plugins); + const options = mockOptions( + { + numTimes: 20, + timerangeCount: 30, + timerangeUnit: 'h', + filters: { + 'monitor.type': ['http'], + 'observer.geo.name': [], + tags: [], + 'url.port': [], + }, + search: 'url.full: *', + }, + undefined, + undefined, + { schedule: { interval: '60h' } } + ); + await alert.executor(options); + + expect(mockGetter).toHaveBeenCalledTimes(1); + expect(mockGetter.mock.calls[0][0]).toEqual( + expect.objectContaining({ + timespanRange: { + from: 'now-30h', + to: 'now', + }, + timestampRange: { + from: mockDate.setHours(mockDate.getHours() - 60).valueOf(), // 60h rule schedule interval is larger than 30h timerange, so use now - 60h to define timestamp range + to: 'now', + }, + }) + ); + }); + describe('hasFilters', () => { it('returns false for undefined filters', () => { expect(hasFilters()).toBe(false); diff --git a/x-pack/plugins/uptime/server/lib/alerts/status_check.ts b/x-pack/plugins/uptime/server/lib/alerts/status_check.ts index 6f3e3303f6bdc..364518bba720a 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/status_check.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/status_check.ts @@ -4,7 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import datemath from '@elastic/datemath'; +import { min } from 'lodash'; import { schema } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; import Mustache from 'mustache'; @@ -31,6 +32,34 @@ import { UMServerLibs, UptimeESClient } from '../lib'; export type ActionGroupIds = ActionGroupIdsOf; +/** + * Returns the appropriate range for filtering the documents by `@timestamp`. + * + * We check monitor status by `monitor.timespan`, but need to first cut down on the number of documents + * searched by filtering by `@timestamp`. To ensure that we catch as many documents as possible which could + * likely contain a down monitor with a `monitor.timespan` in the given timerange, we create a filter + * range for `@timestamp` that is the greater of either: from now to now - timerange interval - 24 hours + * OR from now to now - rule interval + * @param ruleScheduleLookback - string representing now minus the interval at which the rule is ran + * @param timerangeLookback - string representing now minus the timerange configured by the user for checking down monitors + */ +export function getTimestampRange({ + ruleScheduleLookback, + timerangeLookback, +}: Record<'ruleScheduleLookback' | 'timerangeLookback', string>) { + const scheduleIntervalAbsoluteTime = datemath.parse(ruleScheduleLookback)?.valueOf(); + const defaultIntervalAbsoluteTime = datemath + .parse(timerangeLookback) + ?.subtract('24', 'hours') + .valueOf(); + const from = min([scheduleIntervalAbsoluteTime, defaultIntervalAbsoluteTime]) ?? 'now-24h'; + + return { + to: 'now', + from, + }; +} + const getMonIdByLoc = (monitorId: string, location: string) => { return monitorId + '-' + location; }; @@ -264,6 +293,9 @@ export const statusCheckAlertFactory: UptimeAlertTypeFactory = ( params: rawParams, state, services: { alertInstanceFactory }, + rule: { + schedule: { interval }, + }, }, uptimeEsClient, }) { @@ -279,14 +311,22 @@ export const statusCheckAlertFactory: UptimeAlertTypeFactory = ( isAutoGenerated, timerange: oldVersionTimeRange, } = rawParams; - const filterString = await formatFilterString(uptimeEsClient, filters, search, libs); - const timerange = oldVersionTimeRange || { - from: `now-${String(timerangeCount) + timerangeUnit}`, + const timespanInterval = `${String(timerangeCount)}${timerangeUnit}`; + + // Range filter for `monitor.timespan`, the range of time the ping is valid + const timespanRange = oldVersionTimeRange || { + from: `now-${timespanInterval}`, to: 'now', }; + // Range filter for `@timestamp`, the time the document was indexed + const timestampRange = getTimestampRange({ + ruleScheduleLookback: `now-${interval}`, + timerangeLookback: timespanRange.from, + }); + let downMonitorsByLocation: GetMonitorStatusResult[] = []; // if oldVersionTimeRange present means it's 7.7 format and @@ -294,7 +334,8 @@ export const statusCheckAlertFactory: UptimeAlertTypeFactory = ( if (!(!oldVersionTimeRange && shouldCheckStatus === false)) { downMonitorsByLocation = await libs.requests.getMonitorStatus({ uptimeEsClient, - timerange, + timespanRange, + timestampRange, numTimes, locations: [], filters: filterString, diff --git a/x-pack/plugins/uptime/server/lib/requests/get_certs.test.ts b/x-pack/plugins/uptime/server/lib/requests/get_certs.test.ts index 333824df174a6..6e06dea0436db 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_certs.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_certs.test.ts @@ -173,7 +173,7 @@ describe('getCerts', () => { "filter": Array [ Object { "exists": Object { - "field": "tls.server", + "field": "tls.server.hash.sha256", }, }, Object { diff --git a/x-pack/plugins/uptime/server/lib/requests/get_certs.ts b/x-pack/plugins/uptime/server/lib/requests/get_certs.ts index 7639484f51737..86a9825f8a485 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_certs.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_certs.ts @@ -64,7 +64,7 @@ export const getCerts: UMElasticsearchQueryFn = asyn filter: [ { exists: { - field: 'tls.server', + field: 'tls.server.hash.sha256', }, }, { diff --git a/x-pack/plugins/uptime/server/lib/requests/get_monitor_status.test.ts b/x-pack/plugins/uptime/server/lib/requests/get_monitor_status.test.ts index 6d88ccb9a9eff..08b675576a5d2 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_monitor_status.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_monitor_status.test.ts @@ -85,10 +85,14 @@ describe('getMonitorStatus', () => { filters: exampleFilter, locations: [], numTimes: 5, - timerange: { + timespanRange: { from: 'now-10m', to: 'now-1m', }, + timestampRange: { + from: 'now-24h', + to: 'now', + }, }); expect(esMock.search).toHaveBeenCalledTimes(1); const [params] = esMock.search.mock.calls[0]; @@ -144,6 +148,14 @@ describe('getMonitorStatus', () => { Object { "range": Object { "@timestamp": Object { + "gte": "now-24h", + "lte": "now", + }, + }, + }, + Object { + "range": Object { + "monitor.timespan": Object { "gte": "now-10m", "lte": "now-1m", }, @@ -202,10 +214,14 @@ describe('getMonitorStatus', () => { uptimeEsClient, locations: ['fairbanks', 'harrisburg'], numTimes: 1, - timerange: { + timespanRange: { from: 'now-2m', to: 'now', }, + timestampRange: { + from: 'now-24h', + to: 'now', + }, }); expect(esMock.search).toHaveBeenCalledTimes(1); const [params] = esMock.search.mock.calls[0]; @@ -261,6 +277,14 @@ describe('getMonitorStatus', () => { Object { "range": Object { "@timestamp": Object { + "gte": "now-24h", + "lte": "now", + }, + }, + }, + Object { + "range": Object { + "monitor.timespan": Object { "gte": "now-2m", "lte": "now", }, @@ -298,10 +322,14 @@ describe('getMonitorStatus', () => { genBucketItem ); const clientParameters = { - timerange: { + timespanRange: { from: 'now-15m', to: 'now', }, + timestampRange: { + from: 'now-24h', + to: 'now', + }, numTimes: 5, locations: [], filters: { @@ -415,6 +443,14 @@ describe('getMonitorStatus', () => { Object { "range": Object { "@timestamp": Object { + "gte": "now-24h", + "lte": "now", + }, + }, + }, + Object { + "range": Object { + "monitor.timespan": Object { "gte": "now-15m", "lte": "now", }, @@ -485,10 +521,14 @@ describe('getMonitorStatus', () => { genBucketItem ); const clientParameters = { - timerange: { + timespanRange: { from: 'now-15m', to: 'now', }, + timestampRange: { + from: 'now-24h', + to: 'now', + }, numTimes: 5, locations: [], filters: { @@ -562,6 +602,14 @@ describe('getMonitorStatus', () => { Object { "range": Object { "@timestamp": Object { + "gte": "now-24h", + "lte": "now", + }, + }, + }, + Object { + "range": Object { + "monitor.timespan": Object { "gte": "now-15m", "lte": "now", }, @@ -618,10 +666,14 @@ describe('getMonitorStatus', () => { filters: undefined, locations: [], numTimes: 5, - timerange: { + timespanRange: { from: 'now-12m', to: 'now-2m', }, + timestampRange: { + from: 'now-24h', + to: 'now', + }, }; const { uptimeEsClient } = getUptimeESMockClient(esMock); @@ -684,6 +736,14 @@ describe('getMonitorStatus', () => { Object { "range": Object { "@timestamp": Object { + "gte": "now-24h", + "lte": "now", + }, + }, + }, + Object { + "range": Object { + "monitor.timespan": Object { "gte": "now-12m", "lte": "now-2m", }, @@ -810,10 +870,14 @@ describe('getMonitorStatus', () => { uptimeEsClient, locations: [], numTimes: 5, - timerange: { + timespanRange: { from: 'now-10m', to: 'now-1m', }, + timestampRange: { + from: 'now-24h', + to: 'now', + }, }); expect(result.length).toBe(8); expect(result).toMatchInlineSnapshot(` diff --git a/x-pack/plugins/uptime/server/lib/requests/get_monitor_status.ts b/x-pack/plugins/uptime/server/lib/requests/get_monitor_status.ts index 07047bd0be7bc..15e6fe30db186 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_monitor_status.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_monitor_status.ts @@ -15,7 +15,8 @@ export interface GetMonitorStatusParams { filters?: JsonObject; locations: string[]; numTimes: number; - timerange: { from: string; to: string }; + timespanRange: { from: string; to: string }; + timestampRange: { from: string | number; to: string }; } export interface GetMonitorStatusResult { @@ -43,7 +44,7 @@ export type AfterKey = Record | undefined; export const getMonitorStatus: UMElasticsearchQueryFn< GetMonitorStatusParams, GetMonitorStatusResult[] -> = async ({ uptimeEsClient, filters, locations, numTimes, timerange: { from, to } }) => { +> = async ({ uptimeEsClient, filters, locations, numTimes, timespanRange, timestampRange }) => { let afterKey: AfterKey; const STATUS = 'down'; @@ -63,8 +64,16 @@ export const getMonitorStatus: UMElasticsearchQueryFn< { range: { '@timestamp': { - gte: from, - lte: to, + gte: timestampRange.from, + lte: timestampRange.to, + }, + }, + }, + { + range: { + 'monitor.timespan': { + gte: timespanRange.from, + lte: timespanRange.to, }, }, }, diff --git a/x-pack/test/accessibility/apps/ml.ts b/x-pack/test/accessibility/apps/ml.ts index 382086728da01..4babe0bd6ff88 100644 --- a/x-pack/test/accessibility/apps/ml.ts +++ b/x-pack/test/accessibility/apps/ml.ts @@ -59,8 +59,7 @@ export default function ({ getService }: FtrProviderContext) { }); }); - // FLAKY: https://github.com/elastic/kibana/issues/103538 - describe.skip('with data loaded', function () { + describe('with data loaded', function () { const adJobId = 'fq_single_a11y'; const dfaOutlierJobId = 'iph_outlier_a11y'; const calendarId = 'calendar_a11y'; diff --git a/x-pack/test/api_integration/apis/lens/field_stats.ts b/x-pack/test/api_integration/apis/lens/field_stats.ts index 5dcb749f54b31..5090fe14576d5 100644 --- a/x-pack/test/api_integration/apis/lens/field_stats.ts +++ b/x-pack/test/api_integration/apis/lens/field_stats.ts @@ -427,6 +427,38 @@ export default ({ getService }: FtrProviderContext) => { expect(body.totalDocuments).to.eql(425); }); + + it('should allow filtering on a runtime field other than the field in use', async () => { + const { body } = await supertest + .post('/api/lens/index_stats/logstash-2015.09.22/field') + .set(COMMON_HEADERS) + .send({ + dslQuery: { + bool: { + filter: [{ exists: { field: 'runtime_string_field' } }], + }, + }, + fromDate: TEST_START_TIME, + toDate: TEST_END_TIME, + fieldName: 'runtime_number_field', + }) + .expect(200); + + expect(body).to.eql({ + totalDocuments: 4634, + sampledDocuments: 4634, + sampledValues: 4634, + topValues: { + buckets: [ + { + count: 4634, + key: 5, + }, + ], + }, + histogram: { buckets: [] }, + }); + }); }); describe('histogram', () => { diff --git a/x-pack/test/api_integration/apis/ml/data_frame_analytics/get_spaces.ts b/x-pack/test/api_integration/apis/ml/data_frame_analytics/get_spaces.ts index 33dcec6264501..b650943883196 100644 --- a/x-pack/test/api_integration/apis/ml/data_frame_analytics/get_spaces.ts +++ b/x-pack/test/api_integration/apis/ml/data_frame_analytics/get_spaces.ts @@ -45,6 +45,19 @@ export default ({ getService }: FtrProviderContext) => { return body; } + async function runMapRequest(space: string, expectedStatusCode: number, jobId: string) { + const { body } = await supertest + .get(`/s/${space}/api/ml/data_frame/analytics/map/${jobId}`) + .auth( + USER.ML_VIEWER_ALL_SPACES, + ml.securityCommon.getPasswordForUser(USER.ML_VIEWER_ALL_SPACES) + ) + .set(COMMON_REQUEST_HEADERS) + .expect(expectedStatusCode); + + return body; + } + describe('GET data_frame/analytics with spaces', function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ihp_outlier'); @@ -140,5 +153,62 @@ export default ({ getService }: FtrProviderContext) => { it('should fail to list jobs stats by job ids if one of them is in a different space', async () => { await runRequest(idSpace1, 404, true, `${jobIdSpace1},${jobIdSpace2}`); }); + + describe('GetDataFrameAnalyticsIdMap with spaces', () => { + it(`should return a map of objects from ${idSpace1} leading up to analytics job id created in ${idSpace1}`, async () => { + const body = await runMapRequest(idSpace1, 200, jobIdSpace1); + + expect(body).to.have.keys('elements', 'details', 'error'); + // Index node, 2 job nodes (with same source index), and 2 edge nodes to connect them + expect(body.elements.length).to.eql( + 5, + `Expected 5 map elements, got ${body.elements.length}` + ); + expect(body.error).to.be(null); + // No space2 related job ids should be returned + for (const detailsId in body.details) { + if (detailsId.includes('analytics')) { + expect(body.details[detailsId].id.includes(idSpace2)).to.eql( + false, + `No space2 related job ids should be returned, got ${body.details[detailsId].id}` + ); + } + } + }); + + it(`should return a map of objects from ${idSpace2} leading up to analytics job id created in ${idSpace2}`, async () => { + const body = await runMapRequest(idSpace2, 200, jobIdSpace2); + + expect(body).to.have.keys('elements', 'details', 'error'); + // Index node, 1 job node and 2 edge nodes to connect them + expect(body.elements.length).to.eql( + 3, + `Expected 3 map elements, got ${body.elements.length}` + ); + expect(body.error).to.be(null); + // No space1 related job ids should be returned + for (const detailsId in body.details) { + if (detailsId.includes('analytics')) { + expect(body.details[detailsId].id.includes(idSpace1)).to.eql( + false, + `No space1 related job ids should be returned, got ${body.details[detailsId].id}` + ); + } + } + }); + + it(`should fail to return a map of objects from one space when requesting with analytics job id created in a different space`, async () => { + const body = await runMapRequest(idSpace2, 200, jobIdSpace1); + + expect(body).to.have.keys('elements', 'details', 'error'); + + expect(body.elements.length).to.eql( + 0, + `Expected 0 map elements, got ${body.elements.length}` + ); + expect(body.details).to.eql({}); + expect(body.error).to.eql(`No known job with id '${jobIdSpace1}'`); + }); + }); }); }; diff --git a/x-pack/test/api_integration/apis/ml/data_visualizer/get_overall_stats.ts b/x-pack/test/api_integration/apis/ml/data_visualizer/get_overall_stats.ts index 4ce9d4871246c..7987875a75519 100644 --- a/x-pack/test/api_integration/apis/ml/data_visualizer/get_overall_stats.ts +++ b/x-pack/test/api_integration/apis/ml/data_visualizer/get_overall_stats.ts @@ -52,6 +52,7 @@ export default ({ getService }: FtrProviderContext) => { aggregatableNotExistsFields: [{ fieldName: 'sourcetype', existsInDocs: false }], nonAggregatableExistsFields: [{ fieldName: 'type', existsInDocs: true, stats: {} }], nonAggregatableNotExistsFields: [], + errors: [], }, }, }, @@ -98,6 +99,7 @@ export default ({ getService }: FtrProviderContext) => { aggregatableNotExistsFields: [{ fieldName: 'sourcetype', existsInDocs: false }], nonAggregatableExistsFields: [{ fieldName: 'type', existsInDocs: true, stats: {} }], nonAggregatableNotExistsFields: [], + errors: [], }, }, }, diff --git a/x-pack/test/apm_api_integration/tests/correlations/latency_ml.ts b/x-pack/test/apm_api_integration/tests/correlations/latency_ml.ts new file mode 100644 index 0000000000000..cc8f48fb58944 --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/correlations/latency_ml.ts @@ -0,0 +1,266 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import request from 'superagent'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { registry } from '../../common/registry'; + +import { PartialSearchRequest } from '../../../../plugins/apm/server/lib/search_strategies/correlations/search_strategy'; + +function parseBfetchResponse(resp: request.Response): Array> { + return resp.text + .trim() + .split('\n') + .map((item) => JSON.parse(item)); +} + +export default function ApiTest({ getService }: FtrProviderContext) { + const retry = getService('retry'); + const supertest = getService('supertest'); + + const getRequestBody = () => { + const partialSearchRequest: PartialSearchRequest = { + params: { + index: 'apm-*', + environment: 'ENVIRONMENT_ALL', + start: '2020', + end: '2021', + percentileThreshold: 95, + }, + }; + + return { + batch: [ + { + request: partialSearchRequest, + options: { strategy: 'apmCorrelationsSearchStrategy' }, + }, + ], + }; + }; + + registry.when( + 'correlations latency_ml overall without data', + { config: 'trial', archives: [] }, + () => { + it('handles the empty state', async () => { + const intialResponse = await supertest + .post(`/internal/bsearch`) + .set('kbn-xsrf', 'foo') + .send(getRequestBody()); + + expect(intialResponse.status).to.eql( + 200, + `Expected status to be '200', got '${intialResponse.status}'` + ); + expect(intialResponse.body).to.eql( + {}, + `Expected response body to be an empty object, actual response is in the text attribute. Got: '${JSON.stringify( + intialResponse.body + )}'` + ); + + const body = parseBfetchResponse(intialResponse)[0]; + + expect(typeof body.result).to.be('object'); + const { result } = body; + + expect(typeof result?.id).to.be('string'); + + // pass on id for follow up queries + const searchStrategyId = result.id; + + // follow up request body including search strategy ID + const reqBody = getRequestBody(); + reqBody.batch[0].request.id = searchStrategyId; + + let followUpResponse: Record = {}; + + // continues querying until the search strategy finishes + await retry.waitForWithTimeout( + 'search strategy eventually completes and returns full results', + 5000, + async () => { + const response = await supertest + .post(`/internal/bsearch`) + .set('kbn-xsrf', 'foo') + .send(reqBody); + + followUpResponse = parseBfetchResponse(response)[0]; + + return ( + followUpResponse?.result?.isRunning === false || followUpResponse?.error !== undefined + ); + } + ); + + expect(followUpResponse?.error).to.eql( + undefined, + `search strategy should not return an error, got: ${JSON.stringify( + followUpResponse?.error + )}` + ); + + const followUpResult = followUpResponse.result; + expect(followUpResult?.isRunning).to.eql(false, 'search strategy should not be running'); + expect(followUpResult?.isPartial).to.eql( + false, + 'search strategy result should not be partial' + ); + expect(followUpResult?.id).to.eql( + searchStrategyId, + 'search strategy id should match original id' + ); + expect(followUpResult?.isRestored).to.eql( + true, + 'search strategy response should be restored' + ); + expect(followUpResult?.loaded).to.eql(100, 'loaded state should be 100'); + expect(followUpResult?.total).to.eql(100, 'total state should be 100'); + + expect(typeof followUpResult?.rawResponse).to.be('object'); + + const { rawResponse: finalRawResponse } = followUpResult; + + expect(typeof finalRawResponse?.took).to.be('number'); + expect(finalRawResponse?.percentileThresholdValue).to.be(undefined); + expect(finalRawResponse?.overallHistogram).to.be(undefined); + expect(finalRawResponse?.values.length).to.be(0); + expect(finalRawResponse?.log.map((d: string) => d.split(': ')[1])).to.eql([ + 'Fetched 95th percentile value of undefined based on 0 documents.', + 'Abort service since percentileThresholdValue could not be determined.', + ]); + }); + } + ); + + registry.when( + 'Correlations latency_ml with data and opbeans-node args', + { config: 'trial', archives: ['ml_8.0.0'] }, + () => { + // putting this into a single `it` because the responses depend on each other + it('queries the search strategy and returns results', async () => { + const intialResponse = await supertest + .post(`/internal/bsearch`) + .set('kbn-xsrf', 'foo') + .send(getRequestBody()); + + expect(intialResponse.status).to.eql( + 200, + `Expected status to be '200', got '${intialResponse.status}'` + ); + expect(intialResponse.body).to.eql( + {}, + `Expected response body to be an empty object, actual response is in the text attribute. Got: '${JSON.stringify( + intialResponse.body + )}'` + ); + + const body = parseBfetchResponse(intialResponse)[0]; + + expect(typeof body?.result).to.be('object'); + const { result } = body; + + expect(typeof result?.id).to.be('string'); + + // pass on id for follow up queries + const searchStrategyId = result.id; + + expect(result?.loaded).to.be(0); + expect(result?.total).to.be(100); + expect(result?.isRunning).to.be(true); + expect(result?.isPartial).to.be(true); + expect(result?.isRestored).to.eql( + false, + `Expected response result to be not restored. Got: '${result?.isRestored}'` + ); + expect(typeof result?.rawResponse).to.be('object'); + + const { rawResponse } = result; + + expect(typeof rawResponse?.took).to.be('number'); + expect(rawResponse?.values).to.eql([]); + + // follow up request body including search strategy ID + const reqBody = getRequestBody(); + reqBody.batch[0].request.id = searchStrategyId; + + let followUpResponse: Record = {}; + + // continues querying until the search strategy finishes + await retry.waitForWithTimeout( + 'search strategy eventually completes and returns full results', + 5000, + async () => { + const response = await supertest + .post(`/internal/bsearch`) + .set('kbn-xsrf', 'foo') + .send(reqBody); + followUpResponse = parseBfetchResponse(response)[0]; + + return ( + followUpResponse?.result?.isRunning === false || followUpResponse?.error !== undefined + ); + } + ); + + expect(followUpResponse?.error).to.eql( + undefined, + `Finished search strategy should not return an error, got: ${JSON.stringify( + followUpResponse?.error + )}` + ); + + const followUpResult = followUpResponse.result; + expect(followUpResult?.isRunning).to.eql( + false, + `Expected finished result not to be running. Got: ${followUpResult?.isRunning}` + ); + expect(followUpResult?.isPartial).to.eql( + false, + `Expected finished result not to be partial. Got: ${followUpResult?.isPartial}` + ); + expect(followUpResult?.id).to.be(searchStrategyId); + expect(followUpResult?.isRestored).to.be(true); + expect(followUpResult?.loaded).to.be(100); + expect(followUpResult?.total).to.be(100); + + expect(typeof followUpResult?.rawResponse).to.be('object'); + + const { rawResponse: finalRawResponse } = followUpResult; + + expect(typeof finalRawResponse?.took).to.be('number'); + expect(finalRawResponse?.percentileThresholdValue).to.be(1404927.875); + expect(finalRawResponse?.overallHistogram.length).to.be(101); + + expect(finalRawResponse?.values.length).to.eql( + 1, + `Expected 1 identified correlations, got ${finalRawResponse?.values.length}.` + ); + expect(finalRawResponse?.log.map((d: string) => d.split(': ')[1])).to.eql([ + 'Fetched 95th percentile value of 1404927.875 based on 989 documents.', + 'Loaded histogram range steps.', + 'Loaded overall histogram chart data.', + 'Loaded percentiles.', + 'Identified 67 fieldCandidates.', + 'Identified 339 fieldValuePairs.', + 'Loaded fractions and totalDocCount of 989.', + 'Identified 1 significant correlations out of 339 field/value pairs.', + ]); + + const correlation = finalRawResponse?.values[0]; + expect(typeof correlation).to.be('object'); + expect(correlation?.field).to.be('transaction.result'); + expect(correlation?.value).to.be('success'); + expect(correlation?.correlation).to.be(0.37418510688551887); + expect(correlation?.ksTest).to.be(1.1238496968312214e-10); + expect(correlation?.histogram.length).to.be(101); + }); + } + ); +} diff --git a/x-pack/test/apm_api_integration/tests/index.ts b/x-pack/test/apm_api_integration/tests/index.ts index 813e0e4f3cdb8..a00fa1723fa3e 100644 --- a/x-pack/test/apm_api_integration/tests/index.ts +++ b/x-pack/test/apm_api_integration/tests/index.ts @@ -32,6 +32,10 @@ export default function apmApiIntegrationTests(providerContext: FtrProviderConte loadTestFile(require.resolve('./correlations/latency_slow_transactions')); }); + describe('correlations/latency_ml', function () { + loadTestFile(require.resolve('./correlations/latency_ml')); + }); + describe('correlations/latency_overall', function () { loadTestFile(require.resolve('./correlations/latency_overall')); }); 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 05b097cc87b61..e2251b0b8af08 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 @@ -47,9 +47,10 @@ import { getSignalsByIds, findImmutableRuleById, getPrePackagedRulesStatus, - getRuleForSignalTesting, getOpenSignals, createRuleWithExceptionEntries, + getEqlRuleForSignalTesting, + getThresholdRuleForSignalTesting, } from '../../utils'; import { ROLES } from '../../../../plugins/security_solution/common/test'; import { createUserAndRole, deleteUserAndRole } from '../roles_users_utils'; @@ -615,10 +616,7 @@ export default ({ getService }: FtrProviderContext) => { it('generates no signals when an exception is added for an EQL rule', async () => { const rule: EqlCreateSchema = { - ...getRuleForSignalTesting(['auditbeat-*']), - rule_id: 'eql-rule', - type: 'eql', - language: 'eql', + ...getEqlRuleForSignalTesting(['auditbeat-*']), query: 'configuration where agent.id=="a1d7b39c-f898-4dbe-a761-efb61939302d"', }; const createdRule = await createRuleWithExceptionEntries(supertest, rule, [ @@ -637,11 +635,7 @@ export default ({ getService }: FtrProviderContext) => { it('generates no signals when an exception is added for a threshold rule', async () => { const rule: ThresholdCreateSchema = { - ...getRuleForSignalTesting(['auditbeat-*']), - rule_id: 'threshold-rule', - type: 'threshold', - language: 'kuery', - query: '*:*', + ...getThresholdRuleForSignalTesting(['auditbeat-*']), threshold: { field: 'host.id', value: 700, 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 a1a97ac8bfd35..4972b485be06c 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 @@ -21,11 +21,13 @@ import { createSignalsIndex, deleteAllAlerts, deleteSignalsIndex, + getEqlRuleForSignalTesting, getOpenSignals, getRuleForSignalTesting, getSignalsByIds, getSignalsByRuleIds, getSimpleRule, + getThresholdRuleForSignalTesting, waitForRuleSuccessOrStatus, waitForSignalsToBePresent, } from '../../utils'; @@ -273,16 +275,13 @@ export default ({ getService }: FtrProviderContext) => { describe('EQL Rules', () => { it('generates a correctly formatted signal from EQL non-sequence queries', async () => { const rule: EqlCreateSchema = { - ...getRuleForSignalTesting(['auditbeat-*']), - rule_id: 'eql-rule', - type: 'eql', - language: 'eql', + ...getEqlRuleForSignalTesting(['auditbeat-*']), query: 'configuration where agent.id=="a1d7b39c-f898-4dbe-a761-efb61939302d"', }; const { id } = await createRule(supertest, rule); await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); - const signals = await getSignalsByRuleIds(supertest, ['eql-rule']); + const signals = await getSignalsByIds(supertest, [id]); expect(signals.hits.hits.length).eql(1); const fullSignal = signals.hits.hits[0]._source; @@ -393,13 +392,7 @@ export default ({ getService }: FtrProviderContext) => { }); it('generates up to max_signals for non-sequence EQL queries', async () => { - const rule: EqlCreateSchema = { - ...getRuleForSignalTesting(['auditbeat-*']), - rule_id: 'eql-rule', - type: 'eql', - language: 'eql', - query: 'any where true', - }; + const rule: EqlCreateSchema = getEqlRuleForSignalTesting(['auditbeat-*']); const { id } = await createRule(supertest, rule); await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 100, [id]); @@ -412,17 +405,14 @@ export default ({ getService }: FtrProviderContext) => { it('uses the provided event_category_override', async () => { const rule: EqlCreateSchema = { - ...getRuleForSignalTesting(['auditbeat-*']), - rule_id: 'eql-rule', - type: 'eql', - language: 'eql', + ...getEqlRuleForSignalTesting(['auditbeat-*']), query: 'config_change where agent.id=="a1d7b39c-f898-4dbe-a761-efb61939302d"', event_category_override: 'auditd.message_type', }; const { id } = await createRule(supertest, rule); await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); - const signals = await getSignalsByRuleIds(supertest, ['eql-rule']); + const signals = await getSignalsByIds(supertest, [id]); expect(signals.hits.hits.length).eql(1); const fullSignal = signals.hits.hits[0]._source; @@ -534,16 +524,13 @@ export default ({ getService }: FtrProviderContext) => { it('generates building block signals from EQL sequences in the expected form', async () => { const rule: EqlCreateSchema = { - ...getRuleForSignalTesting(['auditbeat-*']), - rule_id: 'eql-rule', - type: 'eql', - language: 'eql', + ...getEqlRuleForSignalTesting(['auditbeat-*']), query: 'sequence by host.name [anomoly where true] [any where true]', }; const { id } = await createRule(supertest, rule); await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); - const signals = await getSignalsByRuleIds(supertest, ['eql-rule']); + const signals = await getSignalsByIds(supertest, [id]); const buildingBlock = signals.hits.hits.find( (signal) => signal._source.signal.depth === 1 && @@ -699,16 +686,13 @@ export default ({ getService }: FtrProviderContext) => { it('generates shell signals from EQL sequences in the expected form', async () => { const rule: EqlCreateSchema = { - ...getRuleForSignalTesting(['auditbeat-*']), - rule_id: 'eql-rule', - type: 'eql', - language: 'eql', + ...getEqlRuleForSignalTesting(['auditbeat-*']), query: 'sequence by host.name [anomoly where true] [any where true]', }; const { id } = await createRule(supertest, rule); await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); - const signalsOpen = await getSignalsByRuleIds(supertest, ['eql-rule']); + const signalsOpen = await getSignalsByIds(supertest, [id]); const sequenceSignal = signalsOpen.hits.hits.find( (signal) => signal._source.signal.depth === 2 ); @@ -802,10 +786,7 @@ export default ({ getService }: FtrProviderContext) => { it('generates up to max_signals with an EQL rule', async () => { const rule: EqlCreateSchema = { - ...getRuleForSignalTesting(['auditbeat-*']), - rule_id: 'eql-rule', - type: 'eql', - language: 'eql', + ...getEqlRuleForSignalTesting(['auditbeat-*']), query: 'sequence by host.name [any where true] [any where true]', }; const { id } = await createRule(supertest, rule); @@ -829,13 +810,8 @@ export default ({ getService }: FtrProviderContext) => { describe('Threshold Rules', () => { it('generates 1 signal from Threshold rules when threshold is met', async () => { - const ruleId = 'threshold-rule'; const rule: ThresholdCreateSchema = { - ...getRuleForSignalTesting(['auditbeat-*']), - rule_id: ruleId, - type: 'threshold', - language: 'kuery', - query: '*:*', + ...getThresholdRuleForSignalTesting(['auditbeat-*']), threshold: { field: 'host.id', value: 700, @@ -844,7 +820,7 @@ export default ({ getService }: FtrProviderContext) => { const { id } = await createRule(supertest, rule); await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); - const signalsOpen = await getSignalsByRuleIds(supertest, [ruleId]); + const signalsOpen = await getSignalsByIds(supertest, [id]); expect(signalsOpen.hits.hits.length).eql(1); const fullSignal = signalsOpen.hits.hits[0]._source; const eventIds = fullSignal.signal.parents.map((event) => event.id); @@ -895,13 +871,8 @@ export default ({ getService }: FtrProviderContext) => { }); it('generates 2 signals from Threshold rules when threshold is met', async () => { - const ruleId = 'threshold-rule'; const rule: ThresholdCreateSchema = { - ...getRuleForSignalTesting(['auditbeat-*']), - rule_id: ruleId, - type: 'threshold', - language: 'kuery', - query: '*:*', + ...getThresholdRuleForSignalTesting(['auditbeat-*']), threshold: { field: 'host.id', value: 100, @@ -910,17 +881,13 @@ export default ({ getService }: FtrProviderContext) => { const { id } = await createRule(supertest, rule); await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); - const signalsOpen = await getSignalsByRuleIds(supertest, [ruleId]); + const signalsOpen = await getSignalsByIds(supertest, [id]); expect(signalsOpen.hits.hits.length).eql(2); }); it('applies the provided query before bucketing ', async () => { - const ruleId = 'threshold-rule'; const rule: ThresholdCreateSchema = { - ...getRuleForSignalTesting(['auditbeat-*']), - rule_id: ruleId, - type: 'threshold', - language: 'kuery', + ...getThresholdRuleForSignalTesting(['auditbeat-*']), query: 'host.id:"2ab45fc1c41e4c84bbd02202a7e5761f"', threshold: { field: 'process.name', @@ -930,18 +897,13 @@ export default ({ getService }: FtrProviderContext) => { const { id } = await createRule(supertest, rule); await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); - const signalsOpen = await getSignalsByRuleIds(supertest, [ruleId]); + const signalsOpen = await getSignalsByIds(supertest, [id]); expect(signalsOpen.hits.hits.length).eql(1); }); it('generates no signals from Threshold rules when threshold is met and cardinality is not met', async () => { - const ruleId = 'threshold-rule'; const rule: ThresholdCreateSchema = { - ...getRuleForSignalTesting(['auditbeat-*']), - rule_id: ruleId, - type: 'threshold', - language: 'kuery', - query: '*:*', + ...getThresholdRuleForSignalTesting(['auditbeat-*']), threshold: { field: 'host.id', value: 100, @@ -959,13 +921,8 @@ export default ({ getService }: FtrProviderContext) => { }); it('generates no signals from Threshold rules when cardinality is met and threshold is not met', async () => { - const ruleId = 'threshold-rule'; const rule: ThresholdCreateSchema = { - ...getRuleForSignalTesting(['auditbeat-*']), - rule_id: ruleId, - type: 'threshold', - language: 'kuery', - query: '*:*', + ...getThresholdRuleForSignalTesting(['auditbeat-*']), threshold: { field: 'host.id', value: 1000, @@ -983,13 +940,8 @@ export default ({ getService }: FtrProviderContext) => { }); it('generates signals from Threshold rules when threshold and cardinality are both met', async () => { - const ruleId = 'threshold-rule'; const rule: ThresholdCreateSchema = { - ...getRuleForSignalTesting(['auditbeat-*']), - rule_id: ruleId, - type: 'threshold', - language: 'kuery', - query: '*:*', + ...getThresholdRuleForSignalTesting(['auditbeat-*']), threshold: { field: 'host.id', value: 100, @@ -1059,13 +1011,8 @@ export default ({ getService }: FtrProviderContext) => { }); it('should not generate signals if only one field meets the threshold requirement', async () => { - const ruleId = 'threshold-rule'; const rule: ThresholdCreateSchema = { - ...getRuleForSignalTesting(['auditbeat-*']), - rule_id: ruleId, - type: 'threshold', - language: 'kuery', - query: '*:*', + ...getThresholdRuleForSignalTesting(['auditbeat-*']), threshold: { field: ['host.id', 'process.name'], value: 22, @@ -1077,13 +1024,8 @@ export default ({ getService }: FtrProviderContext) => { }); it('generates signals from Threshold rules when bucketing by multiple fields', async () => { - const ruleId = 'threshold-rule'; const rule: ThresholdCreateSchema = { - ...getRuleForSignalTesting(['auditbeat-*']), - rule_id: ruleId, - type: 'threshold', - language: 'kuery', - query: '*:*', + ...getThresholdRuleForSignalTesting(['auditbeat-*']), threshold: { field: ['host.id', 'process.name', 'event.module'], value: 21, diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/const_keyword.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/const_keyword.ts index b793fc635843e..7d1a4d01fe27c 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/const_keyword.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/const_keyword.ts @@ -17,8 +17,10 @@ import { createSignalsIndex, deleteAllAlerts, deleteSignalsIndex, + getEqlRuleForSignalTesting, getRuleForSignalTesting, getSignalsById, + getThresholdRuleForSignalTesting, waitForRuleSuccessOrStatus, waitForSignalsToBePresent, } from '../../../utils'; @@ -84,10 +86,7 @@ export default ({ getService }: FtrProviderContext) => { describe('"eql" rule type', () => { it('should detect the "dataset_name_1" from "event.dataset" and have 4 signals', async () => { const rule: EqlCreateSchema = { - ...getRuleForSignalTesting(['const_keyword']), - rule_id: 'eql-rule', - type: 'eql', - language: 'eql', + ...getEqlRuleForSignalTesting(['const_keyword']), query: 'any where event.dataset=="dataset_name_1"', }; @@ -100,10 +99,7 @@ export default ({ getService }: FtrProviderContext) => { it('should copy the "dataset_name_1" from "event.dataset"', async () => { const rule: EqlCreateSchema = { - ...getRuleForSignalTesting(['const_keyword']), - rule_id: 'eql-rule', - type: 'eql', - language: 'eql', + ...getEqlRuleForSignalTesting(['const_keyword']), query: 'any where event.dataset=="dataset_name_1"', }; @@ -126,11 +122,7 @@ export default ({ getService }: FtrProviderContext) => { describe('"threshold" rule type', async () => { it('should detect the "dataset_name_1" from "event.dataset"', async () => { const rule: ThresholdCreateSchema = { - ...getRuleForSignalTesting(['const_keyword']), - rule_id: 'threshold-rule', - type: 'threshold', - language: 'kuery', - query: '*:*', + ...getThresholdRuleForSignalTesting(['const_keyword']), threshold: { field: 'event.dataset', value: 1, diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/keyword.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/keyword.ts index d2d2898587ee2..fba13c95c66ac 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/keyword.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/keyword.ts @@ -13,13 +13,16 @@ import { createSignalsIndex, deleteAllAlerts, deleteSignalsIndex, + getEqlRuleForSignalTesting, getRuleForSignalTesting, getSignalsById, + getThresholdRuleForSignalTesting, waitForRuleSuccessOrStatus, waitForSignalsToBePresent, } from '../../../utils'; import { EqlCreateSchema, + QueryCreateSchema, ThresholdCreateSchema, } from '../../../../../plugins/security_solution/common/detection_engine/schemas/request'; @@ -47,7 +50,7 @@ export default ({ getService }: FtrProviderContext) => { describe('"kql" rule type', () => { it('should detect the "dataset_name_1" from "event.dataset"', async () => { - const rule = { + const rule: QueryCreateSchema = { ...getRuleForSignalTesting(['keyword']), query: 'event.dataset: "dataset_name_1"', }; @@ -70,10 +73,7 @@ export default ({ getService }: FtrProviderContext) => { describe('"eql" rule type', () => { it('should detect the "dataset_name_1" from "event.dataset"', async () => { const rule: EqlCreateSchema = { - ...getRuleForSignalTesting(['keyword']), - rule_id: 'eql-rule', - type: 'eql', - language: 'eql', + ...getEqlRuleForSignalTesting(['keyword']), query: 'any where event.dataset=="dataset_name_1"', }; @@ -96,11 +96,7 @@ export default ({ getService }: FtrProviderContext) => { describe('"threshold" rule type', async () => { it('should detect the "dataset_name_1" from "event.dataset"', async () => { const rule: ThresholdCreateSchema = { - ...getRuleForSignalTesting(['keyword']), - rule_id: 'threshold-rule', - type: 'threshold', - language: 'kuery', - query: '*:*', + ...getThresholdRuleForSignalTesting(['keyword']), threshold: { field: 'event.dataset', value: 1, diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/keyword_mixed_with_const.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/keyword_mixed_with_const.ts index 2ce88da13afab..2a354a83a10ae 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/keyword_mixed_with_const.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/keyword_mixed_with_const.ts @@ -17,6 +17,7 @@ import { createSignalsIndex, deleteAllAlerts, deleteSignalsIndex, + getEqlRuleForSignalTesting, getRuleForSignalTesting, getSignalsById, waitForRuleSuccessOrStatus, @@ -90,10 +91,7 @@ export default ({ getService }: FtrProviderContext) => { describe('"eql" rule type', () => { it('should detect the "dataset_name_1" from "event.dataset" and have 8 signals, 4 from each index', async () => { const rule: EqlCreateSchema = { - ...getRuleForSignalTesting(['keyword', 'const_keyword']), - rule_id: 'eql-rule', - type: 'eql', - language: 'eql', + ...getEqlRuleForSignalTesting(['keyword', 'const_keyword']), query: 'any where event.dataset=="dataset_name_1"', }; @@ -106,10 +104,7 @@ export default ({ getService }: FtrProviderContext) => { it('should copy the "dataset_name_1" from "event.dataset"', async () => { const rule: EqlCreateSchema = { - ...getRuleForSignalTesting(['keyword', 'const_keyword']), - rule_id: 'eql-rule', - type: 'eql', - language: 'eql', + ...getEqlRuleForSignalTesting(['keyword', 'const_keyword']), query: 'any where event.dataset=="dataset_name_1"', }; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/timestamps.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/timestamps.ts index 8645fec287b07..2c304803ded89 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/timestamps.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/timestamps.ts @@ -7,7 +7,10 @@ import expect from '@kbn/expect'; import { orderBy } from 'lodash'; -import { QueryCreateSchema } from '../../../../plugins/security_solution/common/detection_engine/schemas/request'; +import { + EqlCreateSchema, + QueryCreateSchema, +} from '../../../../plugins/security_solution/common/detection_engine/schemas/request'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { @@ -19,6 +22,7 @@ import { waitForSignalsToBePresent, getRuleForSignalTesting, getSignalsByIds, + getEqlRuleForSignalTesting, } from '../../utils'; // eslint-disable-next-line import/no-default-export @@ -54,27 +58,54 @@ export default ({ getService }: FtrProviderContext) => { ); }); - it('should convert the @timestamp which is epoch_seconds into the correct ISO format', async () => { - const rule = getRuleForSignalTesting(['timestamp_in_seconds']); - const { id } = await createRule(supertest, rule); - await waitForRuleSuccessOrStatus(supertest, id); - await waitForSignalsToBePresent(supertest, 1, [id]); - const signalsOpen = await getSignalsByIds(supertest, [id]); - const hits = signalsOpen.hits.hits.map((hit) => hit._source.signal.original_time).sort(); - expect(hits).to.eql(['2021-06-02T23:33:15.000Z']); + describe('KQL query', () => { + it('should convert the @timestamp which is epoch_seconds into the correct ISO format', async () => { + const rule = getRuleForSignalTesting(['timestamp_in_seconds']); + const { id } = await createRule(supertest, rule); + await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 1, [id]); + const signalsOpen = await getSignalsByIds(supertest, [id]); + const hits = signalsOpen.hits.hits.map((hit) => hit._source.signal.original_time).sort(); + expect(hits).to.eql(['2021-06-02T23:33:15.000Z']); + }); + + it('should still use the @timestamp field even with an override field. It should never use the override field', async () => { + const rule: QueryCreateSchema = { + ...getRuleForSignalTesting(['myfakeindex-5']), + timestamp_override: 'event.ingested', + }; + const { id } = await createRule(supertest, rule); + await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 1, [id]); + const signalsOpen = await getSignalsByIds(supertest, [id]); + const hits = signalsOpen.hits.hits.map((hit) => hit._source.signal.original_time).sort(); + expect(hits).to.eql(['2020-12-16T15:16:18.000Z']); + }); }); - it('should still use the @timestamp field even with an override field. It should never use the override field', async () => { - const rule: QueryCreateSchema = { - ...getRuleForSignalTesting(['myfakeindex-5']), - timestamp_override: 'event.ingested', - }; - const { id } = await createRule(supertest, rule); - await waitForRuleSuccessOrStatus(supertest, id); - await waitForSignalsToBePresent(supertest, 1, [id]); - const signalsOpen = await getSignalsByIds(supertest, [id]); - const hits = signalsOpen.hits.hits.map((hit) => hit._source.signal.original_time).sort(); - expect(hits).to.eql(['2020-12-16T15:16:18.000Z']); + describe('EQL query', () => { + it('should convert the @timestamp which is epoch_seconds into the correct ISO format for EQL', async () => { + const rule = getEqlRuleForSignalTesting(['timestamp_in_seconds']); + const { id } = await createRule(supertest, rule); + await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 1, [id]); + const signalsOpen = await getSignalsByIds(supertest, [id]); + const hits = signalsOpen.hits.hits.map((hit) => hit._source.signal.original_time).sort(); + expect(hits).to.eql(['2021-06-02T23:33:15.000Z']); + }); + + it('should still use the @timestamp field even with an override field. It should never use the override field', async () => { + const rule: EqlCreateSchema = { + ...getEqlRuleForSignalTesting(['myfakeindex-5']), + timestamp_override: 'event.ingested', + }; + const { id } = await createRule(supertest, rule); + await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 1, [id]); + const signalsOpen = await getSignalsByIds(supertest, [id]); + const hits = signalsOpen.hits.hits.map((hit) => hit._source.signal.original_time).sort(); + expect(hits).to.eql(['2020-12-16T15:16:18.000Z']); + }); }); }); @@ -119,73 +150,91 @@ export default ({ getService }: FtrProviderContext) => { ); }); - it('should generate signals with event.ingested, @timestamp and (event.ingested + timestamp)', async () => { - const rule: QueryCreateSchema = { - ...getRuleForSignalTesting(['myfa*']), - timestamp_override: 'event.ingested', - }; + describe('KQL', () => { + it('should generate signals with event.ingested, @timestamp and (event.ingested + timestamp)', async () => { + const rule: QueryCreateSchema = { + ...getRuleForSignalTesting(['myfa*']), + timestamp_override: 'event.ingested', + }; - const { id } = await createRule(supertest, rule); + const { id } = await createRule(supertest, rule); - await waitForRuleSuccessOrStatus(supertest, id, 'partial failure'); - await waitForSignalsToBePresent(supertest, 3, [id]); - const signalsResponse = await getSignalsByIds(supertest, [id], 3); - const signals = signalsResponse.hits.hits.map((hit) => hit._source); - const signalsOrderedByEventId = orderBy(signals, 'signal.parent.id', 'asc'); + await waitForRuleSuccessOrStatus(supertest, id, 'partial failure'); + await waitForSignalsToBePresent(supertest, 3, [id]); + const signalsResponse = await getSignalsByIds(supertest, [id], 3); + const signals = signalsResponse.hits.hits.map((hit) => hit._source); + const signalsOrderedByEventId = orderBy(signals, 'signal.parent.id', 'asc'); - expect(signalsOrderedByEventId.length).equal(3); - }); + expect(signalsOrderedByEventId.length).equal(3); + }); - it('should generate 2 signals with @timestamp', async () => { - const rule: QueryCreateSchema = getRuleForSignalTesting(['myfa*']); + it('should generate 2 signals with @timestamp', async () => { + const rule: QueryCreateSchema = getRuleForSignalTesting(['myfa*']); - const { id } = await createRule(supertest, rule); + const { id } = await createRule(supertest, rule); - await waitForRuleSuccessOrStatus(supertest, id, 'partial failure'); - await waitForSignalsToBePresent(supertest, 2, [id]); - const signalsResponse = await getSignalsByIds(supertest, [id]); - const signals = signalsResponse.hits.hits.map((hit) => hit._source); - const signalsOrderedByEventId = orderBy(signals, 'signal.parent.id', 'asc'); + await waitForRuleSuccessOrStatus(supertest, id, 'partial failure'); + await waitForSignalsToBePresent(supertest, 2, [id]); + const signalsResponse = await getSignalsByIds(supertest, [id]); + const signals = signalsResponse.hits.hits.map((hit) => hit._source); + const signalsOrderedByEventId = orderBy(signals, 'signal.parent.id', 'asc'); - expect(signalsOrderedByEventId.length).equal(2); - }); + expect(signalsOrderedByEventId.length).equal(2); + }); + + it('should generate 2 signals when timestamp override does not exist', async () => { + const rule: QueryCreateSchema = { + ...getRuleForSignalTesting(['myfa*']), + timestamp_override: 'event.fakeingestfield', + }; + const { id } = await createRule(supertest, rule); - it('should generate 2 signals when timestamp override does not exist', async () => { - const rule: QueryCreateSchema = { - ...getRuleForSignalTesting(['myfa*']), - timestamp_override: 'event.fakeingestfield', - }; - const { id } = await createRule(supertest, rule); + await waitForRuleSuccessOrStatus(supertest, id, 'partial failure'); + await waitForSignalsToBePresent(supertest, 2, [id]); + const signalsResponse = await getSignalsByIds(supertest, [id, id]); + const signals = signalsResponse.hits.hits.map((hit) => hit._source); + const signalsOrderedByEventId = orderBy(signals, 'signal.parent.id', 'asc'); - await waitForRuleSuccessOrStatus(supertest, id, 'partial failure'); - await waitForSignalsToBePresent(supertest, 2, [id]); - const signalsResponse = await getSignalsByIds(supertest, [id, id]); - const signals = signalsResponse.hits.hits.map((hit) => hit._source); - const signalsOrderedByEventId = orderBy(signals, 'signal.parent.id', 'asc'); + expect(signalsOrderedByEventId.length).equal(2); + }); - expect(signalsOrderedByEventId.length).equal(2); + /** + * We should not use the timestamp override as the "original_time" as that can cause + * confusion if you have both a timestamp and an override in the source event. Instead the "original_time" + * field should only be overridden by the "timestamp" since when we generate a signal + * and we add a new timestamp to the signal. + */ + it('should NOT use the timestamp override as the "original_time"', async () => { + const rule: QueryCreateSchema = { + ...getRuleForSignalTesting(['myfakeindex-2']), + timestamp_override: 'event.ingested', + }; + const { id } = await createRule(supertest, rule); + + await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 1, [id]); + const signalsResponse = await getSignalsByIds(supertest, [id, id]); + const hits = signalsResponse.hits.hits + .map((hit) => hit._source.signal.original_time) + .sort(); + expect(hits).to.eql([undefined]); + }); }); - /** - * We should not use the timestamp override as the "original_time" as that can cause - * confusion if you have both a timestamp and an override in the source event. Instead the "original_time" - * field should only be overridden by the "timestamp" since when we generate a signal - * and we add a new timestamp to the signal. - */ - it('should NOT use the timestamp override as the "original_time"', async () => { - const rule: QueryCreateSchema = { - ...getRuleForSignalTesting(['myfakeindex-2']), - timestamp_override: 'event.ingested', - }; - const { id } = await createRule(supertest, rule); - - await waitForRuleSuccessOrStatus(supertest, id); - await waitForSignalsToBePresent(supertest, 1, [id]); - const signalsResponse = await getSignalsByIds(supertest, [id, id]); - const hits = signalsResponse.hits.hits - .map((hit) => hit._source.signal.original_time) - .sort(); - expect(hits).to.eql([undefined]); + describe('EQL', () => { + it('should generate 2 signals with @timestamp', async () => { + const rule: EqlCreateSchema = getEqlRuleForSignalTesting(['myfa*']); + + const { id } = await createRule(supertest, rule); + + await waitForRuleSuccessOrStatus(supertest, id, 'partial failure'); + await waitForSignalsToBePresent(supertest, 2, [id]); + const signalsResponse = await getSignalsByIds(supertest, [id]); + const signals = signalsResponse.hits.hits.map((hit) => hit._source); + const signalsOrderedByEventId = orderBy(signals, 'signal.parent.id', 'asc'); + + expect(signalsOrderedByEventId.length).equal(2); + }); }); }); @@ -201,31 +250,33 @@ export default ({ getService }: FtrProviderContext) => { await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts'); }); - /** - * This represents our worst case scenario where this field is not mapped on any index - * We want to check that our logic continues to function within the constraints of search after - * Elasticsearch returns java's long.MAX_VALUE for unmapped date fields - * Javascript does not support numbers this large, but without passing in a number of this size - * The search_after will continue to return the same results and not iterate to the next set - * So to circumvent this limitation of javascript we return the stringified version of Java's - * Long.MAX_VALUE so that search_after does not enter into an infinite loop. - * - * ref: https://github.com/elastic/elasticsearch/issues/28806#issuecomment-369303620 - */ - it('should generate 200 signals when timestamp override does not exist', async () => { - const rule: QueryCreateSchema = { - ...getRuleForSignalTesting(['auditbeat-*']), - timestamp_override: 'event.fakeingested', - max_signals: 200, - }; - - const { id } = await createRule(supertest, rule); - await waitForRuleSuccessOrStatus(supertest, id, 'partial failure'); - await waitForSignalsToBePresent(supertest, 200, [id]); - const signalsResponse = await getSignalsByIds(supertest, [id], 200); - const signals = signalsResponse.hits.hits.map((hit) => hit._source); - - expect(signals.length).equal(200); + describe('KQL', () => { + /** + * This represents our worst case scenario where this field is not mapped on any index + * We want to check that our logic continues to function within the constraints of search after + * Elasticsearch returns java's long.MAX_VALUE for unmapped date fields + * Javascript does not support numbers this large, but without passing in a number of this size + * The search_after will continue to return the same results and not iterate to the next set + * So to circumvent this limitation of javascript we return the stringified version of Java's + * Long.MAX_VALUE so that search_after does not enter into an infinite loop. + * + * ref: https://github.com/elastic/elasticsearch/issues/28806#issuecomment-369303620 + */ + it('should generate 200 signals when timestamp override does not exist', async () => { + const rule: QueryCreateSchema = { + ...getRuleForSignalTesting(['auditbeat-*']), + timestamp_override: 'event.fakeingested', + max_signals: 200, + }; + + const { id } = await createRule(supertest, rule); + await waitForRuleSuccessOrStatus(supertest, id, 'partial failure'); + await waitForSignalsToBePresent(supertest, 200, [id]); + const signalsResponse = await getSignalsByIds(supertest, [id], 200); + const signals = signalsResponse.hits.hits.map((hit) => hit._source); + + expect(signals.length).equal(200); + }); }); }); }); diff --git a/x-pack/test/detection_engine_api_integration/utils.ts b/x-pack/test/detection_engine_api_integration/utils.ts index 54252b19fc940..ac11dd31c15e8 100644 --- a/x-pack/test/detection_engine_api_integration/utils.ts +++ b/x-pack/test/detection_engine_api_integration/utils.ts @@ -27,6 +27,8 @@ import { UpdateRulesSchema, FullResponseSchema, QueryCreateSchema, + EqlCreateSchema, + ThresholdCreateSchema, } from '../../plugins/security_solution/common/detection_engine/schemas/request'; import { Signal } from '../../plugins/security_solution/server/lib/detection_engine/signals/types'; import { signalsMigrationType } from '../../plugins/security_solution/server/lib/detection_engine/migrations/saved_objects'; @@ -123,6 +125,46 @@ export const getRuleForSignalTesting = ( from: '1900-01-01T00:00:00.000Z', }); +/** + * This is a typical signal testing rule that is easy for most basic testing of output of EQL signals. + * It starts out in an enabled true state. The from is set very far back to test the basics of signal + * creation for EQL and testing by getting all the signals at once. + * @param ruleId The optional ruleId which is eql-rule by default. + * @param enabled Enables the rule on creation or not. Defaulted to true. + */ +export const getEqlRuleForSignalTesting = ( + index: string[], + ruleId = 'eql-rule', + enabled = true +): EqlCreateSchema => ({ + ...getRuleForSignalTesting(index, ruleId, enabled), + type: 'eql', + language: 'eql', + query: 'any where true', +}); + +/** + * This is a typical signal testing rule that is easy for most basic testing of output of Threshold signals. + * It starts out in an enabled true state. The from is set very far back to test the basics of signal + * creation for Threshold and testing by getting all the signals at once. + * @param ruleId The optional ruleId which is threshold-rule by default. + * @param enabled Enables the rule on creation or not. Defaulted to true. + */ +export const getThresholdRuleForSignalTesting = ( + index: string[], + ruleId = 'threshold-rule', + enabled = true +): ThresholdCreateSchema => ({ + ...getRuleForSignalTesting(index, ruleId, enabled), + type: 'threshold', + language: 'kuery', + query: '*:*', + threshold: { + field: 'process.name', + value: 21, + }, +}); + export const getRuleForSignalTestingWithTimestampOverride = ( index: string[], ruleId = 'rule-1', diff --git a/x-pack/test/functional/apps/discover/async_scripted_fields.js b/x-pack/test/functional/apps/discover/async_scripted_fields.js index 427d8c21635c4..2c18051405964 100644 --- a/x-pack/test/functional/apps/discover/async_scripted_fields.js +++ b/x-pack/test/functional/apps/discover/async_scripted_fields.js @@ -19,8 +19,7 @@ export default function ({ getService, getPageObjects }) { const queryBar = getService('queryBar'); const security = getService('security'); - // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/104362 - describe.skip('async search with scripted fields', function () { + describe('async search with scripted fields', function () { this.tags(['skipFirefox']); before(async function () { diff --git a/x-pack/test/functional/apps/discover/visualize_field.ts b/x-pack/test/functional/apps/discover/visualize_field.ts index de0dc459b6395..650d67f05129c 100644 --- a/x-pack/test/functional/apps/discover/visualize_field.ts +++ b/x-pack/test/functional/apps/discover/visualize_field.ts @@ -28,8 +28,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await PageObjects.timePicker.setDefaultAbsoluteRange(); } - // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/104469 - describe.skip('discover field visualize button', () => { + describe('discover field visualize button', () => { beforeEach(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/lens/basic'); diff --git a/x-pack/test/functional/apps/lens/dashboard.ts b/x-pack/test/functional/apps/lens/dashboard.ts index 6e4c20744c5fc..095b1ae7c2f0f 100644 --- a/x-pack/test/functional/apps/lens/dashboard.ts +++ b/x-pack/test/functional/apps/lens/dashboard.ts @@ -27,6 +27,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const filterBar = getService('filterBar'); const security = getService('security'); const panelActions = getService('dashboardPanelActions'); + const inspector = getService('inspector'); async function clickInChart(x: number, y: number) { const el = await elasticChart.getCanvas(); @@ -158,6 +159,56 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.existOrFail(ACTION_TEST_SUBJ); }); + it('should show all data from all layers in the inspector', async () => { + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.clickNewDashboard(); + await dashboardAddPanel.clickCreateNewLink(); + await PageObjects.header.waitUntilLoadingHasFinished(); + 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: 'average', + field: 'bytes', + }); + + await PageObjects.lens.createLayer(); + + expect(await PageObjects.lens.hasChartSwitchWarning('line')).to.eql(false); + + await PageObjects.lens.switchToVisualization('line'); + await PageObjects.lens.configureDimension( + { + dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension', + operation: 'date_histogram', + field: '@timestamp', + }, + 1 + ); + + await PageObjects.lens.configureDimension( + { + dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension', + operation: 'median', + field: 'bytes', + }, + 1 + ); + await PageObjects.lens.saveAndReturn(); + + await panelActions.openContextMenu(); + await panelActions.clickContextMenuMoreItem(); + await testSubjects.click('embeddablePanelAction-openInspector'); + await inspector.openInspectorRequestsView(); + const requests = await inspector.getRequestNames(); + expect(requests.split(',').length).to.be(2); + }); + it('unlink lens panel from embeddable library', async () => { await PageObjects.common.navigateToApp('dashboard'); await PageObjects.dashboard.clickNewDashboard(); diff --git a/x-pack/test/functional/apps/lens/formula.ts b/x-pack/test/functional/apps/lens/formula.ts index 6148215d8b6d2..7662b32b8aee6 100644 --- a/x-pack/test/functional/apps/lens/formula.ts +++ b/x-pack/test/functional/apps/lens/formula.ts @@ -15,9 +15,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const browser = getService('browser'); const testSubjects = getService('testSubjects'); const fieldEditor = getService('fieldEditor'); + const retry = getService('retry'); - // FLAKY: https://github.com/elastic/kibana/issues/105016 - describe.skip('lens formula', () => { + describe('lens formula', () => { it('should transition from count to formula', async () => { await PageObjects.visualize.gotoVisualizationLandingPage(); await listingTable.searchForItemWithName('lnsXYvis'); @@ -55,8 +55,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const input = await find.activeElement(); await input.type('*'); - await PageObjects.header.waitUntilLoadingHasFinished(); - expect(await PageObjects.lens.getDatatableCellText(0, 0)).to.eql('14,005'); + await retry.try(async () => { + expect(await PageObjects.lens.getDatatableCellText(0, 0)).to.eql('14,005'); + }); }); it('should insert single quotes and escape when needed to create valid KQL', async () => { @@ -79,15 +80,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.common.sleep(100); - let element = await find.byCssSelector('.monaco-editor'); - expect(await element.getVisibleText()).to.equal(`count(kql='Men\\'s Clothing ')`); + PageObjects.lens.expectFormulaText(`count(kql='Men\\'s Clothing ')`); await PageObjects.lens.typeFormula('count(kql='); + input = await find.activeElement(); await input.type(`Men\'s Clothing`); - element = await find.byCssSelector('.monaco-editor'); - expect(await element.getVisibleText()).to.equal(`count(kql='Men\\'s Clothing')`); + PageObjects.lens.expectFormulaText(`count(kql='Men\\'s Clothing')`); }); it('should insert single quotes and escape when needed to create valid field name', async () => { @@ -109,20 +109,16 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); await PageObjects.lens.switchToFormula(); - let element = await find.byCssSelector('.monaco-editor'); - expect(await element.getVisibleText()).to.equal(`unique_count('*\\' "\\'')`); + PageObjects.lens.expectFormulaText(`unique_count('*\\' "\\'')`); + await PageObjects.lens.typeFormula('unique_count('); const input = await find.activeElement(); - await input.clearValueWithKeyboard({ charByChar: true }); - await input.type('unique_count('); - await PageObjects.common.sleep(100); await input.type('*'); await input.pressKeys(browser.keys.ENTER); await PageObjects.common.sleep(100); - element = await find.byCssSelector('.monaco-editor'); - expect(await element.getVisibleText()).to.equal(`unique_count('*\\' "\\'')`); + PageObjects.lens.expectFormulaText(`unique_count('*\\' "\\'')`); }); it('should persist a broken formula on close', async () => { diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts index 031074876f39c..2e88198adbc08 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts @@ -496,7 +496,8 @@ export default function ({ getService }: FtrProviderContext) { }); } - describe('index based', function () { + // FLAKY: https://github.com/elastic/kibana/issues/105087 + describe.skip('index based', function () { this.tags(['mlqa']); before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); diff --git a/x-pack/test/functional/apps/reporting/reporting.ts b/x-pack/test/functional/apps/reporting/reporting.ts index 9896e3371a282..8a0d9937fc213 100644 --- a/x-pack/test/functional/apps/reporting/reporting.ts +++ b/x-pack/test/functional/apps/reporting/reporting.ts @@ -12,9 +12,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const pageObjects = getPageObjects(['dashboard', 'common', 'reporting']); const es = getService('es'); const esArchiver = getService('esArchiver'); + const retry = getService('retry'); - // FLAKY: https://github.com/elastic/kibana/issues/102722 - describe.skip('Reporting', function () { + describe('Reporting', function () { this.tags(['smoke', 'ciGroup2']); before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/packaging'); @@ -33,6 +33,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { this.timeout(180000); await pageObjects.common.navigateToApp('dashboards'); + await retry.waitFor('dashboard landing page', async () => { + return await pageObjects.dashboard.onDashboardLandingPage(); + }); await pageObjects.dashboard.loadSavedDashboard('dashboard'); await pageObjects.reporting.openPdfReportingPanel(); await pageObjects.reporting.clickGenerateReportButton(); diff --git a/x-pack/test/functional/es_archives/visualize/default/data.json b/x-pack/test/functional/es_archives/visualize/default/data.json index 7d0ad0c25f96d..a16e1676611ce 100644 --- a/x-pack/test/functional/es_archives/visualize/default/data.json +++ b/x-pack/test/functional/es_archives/visualize/default/data.json @@ -157,7 +157,7 @@ "timeFieldName": "@timestamp", "title": "logstash-2015.09.22", "fields":"[{\"name\":\"scripted_date\",\"type\":\"date\",\"count\":0,\"scripted\":true,\"script\":\"1234\",\"lang\":\"painless\",\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"scripted_string\",\"type\":\"string\",\"count\":0,\"scripted\":true,\"script\":\"return 'hello'\",\"lang\":\"painless\",\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false}]", - "runtimeFieldMap":"{\"runtime_string_field\":{\"type\":\"keyword\",\"script\":{\"source\":\"emit('hello world!')\"}}}" + "runtimeFieldMap":"{\"runtime_string_field\":{\"type\":\"keyword\",\"script\":{\"source\":\"emit('hello world!')\"}},\"runtime_number_field\":{\"type\":\"double\",\"script\":{\"source\":\"emit(5)\"}}}" }, "migrationVersion": { "index-pattern": "7.11.0" diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts index e256d5cd651cc..1acddd4641ff4 100644 --- a/x-pack/test/functional/page_objects/lens_page.ts +++ b/x-pack/test/functional/page_objects/lens_page.ts @@ -139,6 +139,8 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont } if (opts.formula) { + // Formula takes time to open + await PageObjects.common.sleep(500); await this.typeFormula(opts.formula); } @@ -1067,13 +1069,18 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont }, async typeFormula(formula: string) { - // Formula takes time to open - await PageObjects.common.sleep(500); await find.byCssSelector('.monaco-editor'); await find.clickByCssSelectorWhenNotDisabled('.monaco-editor'); const input = await find.activeElement(); await input.clearValueWithKeyboard({ charByChar: true }); await input.type(formula); + // Debounce time for formula + await PageObjects.common.sleep(300); + }, + + async expectFormulaText(formula: string) { + const element = await find.byCssSelector('.monaco-editor'); + expect(await element.getVisibleText()).to.equal(formula); }, async filterLegend(value: string) { diff --git a/x-pack/test/reporting_api_integration/reporting_without_security/job_apis_csv.ts b/x-pack/test/reporting_api_integration/reporting_without_security/job_apis_csv.ts index 577afb200d4a1..06f3756593d76 100644 --- a/x-pack/test/reporting_api_integration/reporting_without_security/job_apis_csv.ts +++ b/x-pack/test/reporting_api_integration/reporting_without_security/job_apis_csv.ts @@ -7,18 +7,13 @@ import expect from '@kbn/expect'; import { pick } from 'lodash'; -import { - ReportApiJSON, - ReportDocument, - ReportSource, -} from '../../../plugins/reporting/common/types'; +import { ReportApiJSON } from '../../../plugins/reporting/common/types'; import { FtrProviderContext } from '../ftr_provider_context'; const apiResponseFields = [ 'attempts', 'created_by', 'jobtype', - 'max_attempts', 'meta', 'payload.isDeprecated', 'payload.title', @@ -26,26 +21,13 @@ const apiResponseFields = [ 'status', ]; -// TODO: clean up the /list and /info endpoints to return ReportApiJSON interface data -const documentResponseFields = [ - '_source.attempts', - '_source.created_by', - '_source.jobtype', - '_source.max_attempts', - '_source.meta', - '_source.payload.isDeprecated', - '_source.payload.title', - '_source.payload.type', - '_source.status', -]; - // eslint-disable-next-line import/no-default-export export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertestNoAuth = getService('supertestWithoutAuth'); const reportingAPI = getService('reportingAPI'); - const postJobCSV = async () => { + const postJobCSV = async (): Promise<{ job: ReportApiJSON; path: string }> => { const jobParams = `(browserTimezone:UTC,columns:!('@timestamp',clientip,extension),` + `objectType:search,searchSource:(fields:!((field:'*',include_unmapped:true)),filter:!((meta:(index:'logstash-*',params:()),` + @@ -81,13 +63,12 @@ export default function ({ getService }: FtrProviderContext) { }); it('Posted CSV job is visible in the job count', async () => { - const { job, path }: { job: ReportApiJSON; path: string } = await postJobCSV(); + const { job, path } = await postJobCSV(); expectSnapshot(pick(job, apiResponseFields)).toMatchInline(` Object { "attempts": 0, "created_by": false, "jobtype": "csv_searchsource", - "max_attempts": 1, "meta": Object { "objectType": "search", }, @@ -110,13 +91,12 @@ export default function ({ getService }: FtrProviderContext) { it('Posted CSV job is visible in the status check', async () => { // post a job - const { job, path }: { job: ReportApiJSON; path: string } = await postJobCSV(); + const { job, path } = await postJobCSV(); expectSnapshot(pick(job, apiResponseFields)).toMatchInline(` Object { "attempts": 0, "created_by": false, "jobtype": "csv_searchsource", - "max_attempts": 1, "meta": Object { "objectType": "search", }, @@ -133,24 +113,21 @@ export default function ({ getService }: FtrProviderContext) { .set('kbn-xsrf', 'xxx'); // verify the top item in the list - const listingJobs: ReportDocument[] = JSON.parse(listText); - expect(listingJobs[0]._id).to.be(job.id); - expectSnapshot(listingJobs.map((j) => pick(j, documentResponseFields))).toMatchInline(` + const listingJobs: ReportApiJSON[] = JSON.parse(listText); + expect(listingJobs[0].id).to.be(job.id); + expectSnapshot(listingJobs.map((j) => pick(j, apiResponseFields))).toMatchInline(` Array [ Object { - "_source": Object { - "attempts": 0, - "created_by": false, - "jobtype": "csv_searchsource", - "max_attempts": 1, - "meta": Object { - "objectType": "search", - }, - "payload": Object { - "title": "A Saved Search With a DATE FILTER", - }, - "status": "pending", + "attempts": 0, + "created_by": false, + "jobtype": "csv_searchsource", + "meta": Object { + "objectType": "search", + }, + "payload": Object { + "title": "A Saved Search With a DATE FILTER", }, + "status": "pending", }, ] `); @@ -161,13 +138,12 @@ export default function ({ getService }: FtrProviderContext) { it('Posted CSV job is visible in the first page of jobs listing', async () => { // post a job - const { job, path }: { job: ReportApiJSON; path: string } = await postJobCSV(); + const { job, path } = await postJobCSV(); expectSnapshot(pick(job, apiResponseFields)).toMatchInline(` Object { "attempts": 0, "created_by": false, "jobtype": "csv_searchsource", - "max_attempts": 1, "meta": Object { "objectType": "search", }, @@ -179,29 +155,27 @@ export default function ({ getService }: FtrProviderContext) { `); // call the listing api - const { text: listText } = await supertestNoAuth + const { text: listText, status } = await supertestNoAuth .get(`/api/reporting/jobs/list?page=0`) .set('kbn-xsrf', 'xxx'); + expect(status).to.be(200); // verify the top item in the list - const listingJobs: ReportDocument[] = JSON.parse(listText); - expect(listingJobs[0]._id).to.be(job.id); - expectSnapshot(listingJobs.map((j) => pick(j, documentResponseFields))).toMatchInline(` + const listingJobs: ReportApiJSON[] = JSON.parse(listText); + expect(listingJobs[0].id).to.be(job.id); + expectSnapshot(listingJobs.map((j) => pick(j, apiResponseFields))).toMatchInline(` Array [ Object { - "_source": Object { - "attempts": 0, - "created_by": false, - "jobtype": "csv_searchsource", - "max_attempts": 1, - "meta": Object { - "objectType": "search", - }, - "payload": Object { - "title": "A Saved Search With a DATE FILTER", - }, - "status": "pending", + "attempts": 0, + "created_by": false, + "jobtype": "csv_searchsource", + "meta": Object { + "objectType": "search", + }, + "payload": Object { + "title": "A Saved Search With a DATE FILTER", }, + "status": "pending", }, ] `); @@ -212,13 +186,12 @@ export default function ({ getService }: FtrProviderContext) { it('Posted CSV job details are visible in the info API', async () => { // post a job - const { job, path }: { job: ReportApiJSON; path: string } = await postJobCSV(); + const { job, path } = await postJobCSV(); expectSnapshot(pick(job, apiResponseFields)).toMatchInline(` Object { "attempts": 0, "created_by": false, "jobtype": "csv_searchsource", - "max_attempts": 1, "meta": Object { "objectType": "search", }, @@ -229,17 +202,17 @@ export default function ({ getService }: FtrProviderContext) { } `); - const { text: infoText } = await supertestNoAuth + const { text: infoText, status } = await supertestNoAuth .get(`/api/reporting/jobs/info/${job.id}`) .set('kbn-xsrf', 'xxx'); + expect(status).to.be(200); - const info: ReportSource = JSON.parse(infoText); + const info = JSON.parse(infoText); expectSnapshot(pick(info, apiResponseFields)).toMatchInline(` Object { "attempts": 0, "created_by": false, "jobtype": "csv_searchsource", - "max_attempts": 1, "meta": Object { "objectType": "search", }, diff --git a/x-pack/test/reporting_api_integration/reporting_without_security/job_apis_csv_deprecated.ts b/x-pack/test/reporting_api_integration/reporting_without_security/job_apis_csv_deprecated.ts index 5aafcfb9d29a1..2d62725e23989 100644 --- a/x-pack/test/reporting_api_integration/reporting_without_security/job_apis_csv_deprecated.ts +++ b/x-pack/test/reporting_api_integration/reporting_without_security/job_apis_csv_deprecated.ts @@ -7,11 +7,7 @@ import expect from '@kbn/expect'; import { pick } from 'lodash'; -import { - ReportApiJSON, - ReportDocument, - ReportSource, -} from '../../../plugins/reporting/common/types'; +import { ReportApiJSON } from '../../../plugins/reporting/common/types'; import { FtrProviderContext } from '../ftr_provider_context'; import { JOB_PARAMS_RISON_CSV_DEPRECATED } from '../services/fixtures'; @@ -19,7 +15,6 @@ const apiResponseFields = [ 'attempts', 'created_by', 'jobtype', - 'max_attempts', 'meta', 'payload.isDeprecated', 'payload.title', @@ -27,18 +22,8 @@ const apiResponseFields = [ 'status', ]; -// TODO: clean up the /list and /info endpoints to return ReportApiJSON interface data -const documentResponseFields = [ - '_source.attempts', - '_source.created_by', - '_source.jobtype', - '_source.max_attempts', - '_source.meta', - '_source.payload.isDeprecated', - '_source.payload.title', - '_source.payload.type', - '_source.status', -]; +const parseApiJSON = (apiResponseText: string): { job: ReportApiJSON; path: string } => + JSON.parse(apiResponseText); // eslint-disable-next-line import/no-default-export export default function ({ getService }: FtrProviderContext) { @@ -68,13 +53,12 @@ export default function ({ getService }: FtrProviderContext) { .send({ jobParams: JOB_PARAMS_RISON_CSV_DEPRECATED }); expect(resStatus).to.be(200); - const { job, path }: { job: ReportApiJSON; path: string } = JSON.parse(resText); + const { job, path } = parseApiJSON(resText); expectSnapshot(pick(job, apiResponseFields)).toMatchInline(` Object { "attempts": 0, "created_by": false, "jobtype": "csv", - "max_attempts": 1, "meta": Object {}, "payload": Object { "isDeprecated": true, @@ -103,30 +87,27 @@ export default function ({ getService }: FtrProviderContext) { .send({ jobParams: JOB_PARAMS_RISON_CSV_DEPRECATED }); expect(resStatus).to.be(200); - const { job, path }: { job: ReportApiJSON; path: string } = JSON.parse(resText); + const { job, path } = parseApiJSON(resText); // call the single job listing api (status check) const { text: listText } = await supertestNoAuth .get(`/api/reporting/jobs/list?page=0&ids=${job.id}`) .set('kbn-xsrf', 'xxx'); - const listingJobs: ReportDocument[] = JSON.parse(listText); - expect(listingJobs[0]._id).to.be(job.id); - expectSnapshot(listingJobs.map((j) => pick(j, documentResponseFields))).toMatchInline(` + const listingJobs: ReportApiJSON[] = JSON.parse(listText); + expect(listingJobs[0].id).to.be(job.id); + expectSnapshot(listingJobs.map((j) => pick(j, apiResponseFields))).toMatchInline(` Array [ Object { - "_source": Object { - "attempts": 0, - "created_by": false, - "jobtype": "csv", - "max_attempts": 1, - "meta": Object {}, - "payload": Object { - "isDeprecated": true, - "title": "A Saved Search With a DATE FILTER", - "type": "search", - }, - "status": "pending", + "attempts": 0, + "created_by": false, + "jobtype": "csv", + "meta": Object {}, + "payload": Object { + "isDeprecated": true, + "title": "A Saved Search With a DATE FILTER", + "type": "search", }, + "status": "pending", }, ] `); @@ -141,30 +122,27 @@ export default function ({ getService }: FtrProviderContext) { .send({ jobParams: JOB_PARAMS_RISON_CSV_DEPRECATED }); expect(resStatus).to.be(200); - const { job, path }: { job: ReportApiJSON; path: string } = JSON.parse(resText); + const { job, path } = parseApiJSON(resText); // call the ALL job listing api const { text: listText } = await supertestNoAuth .get(`/api/reporting/jobs/list?page=0`) .set('kbn-xsrf', 'xxx'); - const listingJobs: ReportDocument[] = JSON.parse(listText); - expect(listingJobs[0]._id).to.eql(job.id); - expectSnapshot(listingJobs.map((j) => pick(j, documentResponseFields))).toMatchInline(` + const listingJobs: ReportApiJSON[] = JSON.parse(listText); + expect(listingJobs[0].id).to.eql(job.id); + expectSnapshot(listingJobs.map((j) => pick(j, apiResponseFields))).toMatchInline(` Array [ Object { - "_source": Object { - "attempts": 0, - "created_by": false, - "jobtype": "csv", - "max_attempts": 1, - "meta": Object {}, - "payload": Object { - "isDeprecated": true, - "title": "A Saved Search With a DATE FILTER", - "type": "search", - }, - "status": "pending", + "attempts": 0, + "created_by": false, + "jobtype": "csv", + "meta": Object {}, + "payload": Object { + "isDeprecated": true, + "title": "A Saved Search With a DATE FILTER", + "type": "search", }, + "status": "pending", }, ] `); @@ -179,18 +157,17 @@ export default function ({ getService }: FtrProviderContext) { .send({ jobParams: JOB_PARAMS_RISON_CSV_DEPRECATED }); expect(resStatus).to.be(200); - const { job, path }: { job: ReportApiJSON; path: string } = JSON.parse(resText); + const { job, path } = parseApiJSON(resText); const { text: infoText } = await supertestNoAuth .get(`/api/reporting/jobs/info/${job.id}`) .set('kbn-xsrf', 'xxx'); - const info: ReportSource = JSON.parse(infoText); + const info = JSON.parse(infoText); expectSnapshot(pick(info, apiResponseFields)).toMatchInline(` Object { "attempts": 0, "created_by": false, "jobtype": "csv", - "max_attempts": 1, "meta": Object {}, "payload": Object { "isDeprecated": true, diff --git a/x-pack/test/rule_registry/common/config.ts b/x-pack/test/rule_registry/common/config.ts index 8d1b3807a245b..487af84141d20 100644 --- a/x-pack/test/rule_registry/common/config.ts +++ b/x-pack/test/rule_registry/common/config.ts @@ -80,6 +80,9 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) `--xpack.actions.enabledActionTypes=${JSON.stringify(enabledActionTypes)}`, '--xpack.eventLog.logEntries=true', ...disabledPlugins.map((key) => `--xpack.${key}.enabled=false`), + // TO DO: Remove feature flags once we're good to go + '--xpack.securitySolution.enableExperimental=["ruleRegistryEnabled"]', + '--xpack.ruleRegistry.write.enabled=true', `--server.xsrf.whitelist=${JSON.stringify(getAllExternalServiceSimulatorPaths())}`, ...(ssl ? [ diff --git a/x-pack/test/rule_registry/common/lib/authentication/roles.ts b/x-pack/test/rule_registry/common/lib/authentication/roles.ts index e38378dcfc8f2..098bb649ccb1a 100644 --- a/x-pack/test/rule_registry/common/lib/authentication/roles.ts +++ b/x-pack/test/rule_registry/common/lib/authentication/roles.ts @@ -24,10 +24,7 @@ export const globalRead: Role = { }, kibana: [ { - feature: { - siem: ['read'], - apm: ['read'], - }, + base: ['read'], spaces: ['*'], }, ], diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json index 0424891064cd3..cd43e7108b06d 100644 --- a/x-pack/test/tsconfig.json +++ b/x-pack/test/tsconfig.json @@ -55,7 +55,6 @@ { "path": "../plugins/banners/tsconfig.json" }, { "path": "../plugins/cases/tsconfig.json" }, { "path": "../plugins/cloud/tsconfig.json" }, - { "path": "../plugins/console_extensions/tsconfig.json" }, { "path": "../plugins/dashboard_mode/tsconfig.json" }, { "path": "../plugins/enterprise_search/tsconfig.json" }, { "path": "../plugins/fleet/tsconfig.json" }, diff --git a/yarn.lock b/yarn.lock index 7a70953379f54..0199fabf82043 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1466,10 +1466,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@34.5.2": - version "34.5.2" - resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-34.5.2.tgz#6aad49945a894fb77785a48c281cd0bd5e9e87ba" - integrity sha512-+ColXEaZ8Oa8lJ/ixayiLuWlYUggoTTW0Q5sWXOolR94PlekxsdSpu5f0kVyxlz7ECdkHz3ttOu9RYM7Z6ARyA== +"@elastic/eui@35.0.0": + version "35.0.0" + resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-35.0.0.tgz#e270b4f4ce216b8bba7dc874464bfc76c242e8ce" + integrity sha512-btjkhFb017iY5OM60ka0w+N4TqNeEpsXfaVmvPB5AzYMCT4wLC1L4I2xBD7W2xQbk5fTpKuh9Q6fOk3hmv3iKQ== dependencies: "@types/chroma-js" "^2.0.0" "@types/lodash" "^4.14.160" @@ -1495,7 +1495,7 @@ react-is "~16.3.0" react-virtualized-auto-sizer "^1.0.2" react-window "^1.8.5" - refractor "^3.3.1" + refractor "^3.4.0" rehype-raw "^5.0.0" rehype-react "^6.0.0" rehype-stringify "^8.0.0" @@ -2931,6 +2931,10 @@ version "0.0.0" uid "" +"@kbn/typed-react-router-config@link:bazel-bin/packages/kbn-typed-react-router-config": + version "0.0.0" + uid "" + "@kbn/ui-framework@link:bazel-bin/packages/kbn-ui-framework": version "0.0.0" uid "" @@ -5879,6 +5883,15 @@ dependencies: "@types/react" "*" +"@types/react-router-config@^5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@types/react-router-config/-/react-router-config-5.0.2.tgz#4d3b52e71ed363a1976a12321e67b09a99ad6d10" + integrity sha512-WOSetDV3YPxbkVJAdv/bqExJjmcdCi/vpCJh3NfQOy1X15vHMSiMioXIcGekXDJJYhqGUMDo9e337mh508foAA== + dependencies: + "@types/history" "*" + "@types/react" "*" + "@types/react-router" "*" + "@types/react-router-dom@^5.1.5": version "5.1.5" resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.1.5.tgz#7c334a2ea785dbad2b2dcdd83d2cf3d9973da090" @@ -22286,7 +22299,7 @@ printj@~1.1.0: resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ== -prismjs@1.24.0, prismjs@^1.22.0, prismjs@~1.23.0: +prismjs@^1.22.0, prismjs@~1.24.0: version "1.24.0" resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.24.0.tgz#0409c30068a6c52c89ef7f1089b3ca4de56be2ac" integrity sha512-SqV5GRsNqnzCL8k5dfAjCNhUrF3pR0A9lTDSCUZeh/LIshheXJEaP0hwLz2t4XHivd2J/v2HR+gRnigzeKe3cQ== @@ -23280,6 +23293,13 @@ react-reverse-portal@^1.0.4: resolved "https://registry.yarnpkg.com/react-reverse-portal/-/react-reverse-portal-1.0.4.tgz#d127d2c9147549b25c4959aba1802eca4b144cd4" integrity sha512-WESex/wSjxHwdG7M0uwPNkdQXaLauXNHi4INQiRybmFIXVzAqgf/Ak2OzJ4MLf4UuCD/IzEwJOkML2SxnnontA== +react-router-config@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/react-router-config/-/react-router-config-5.1.1.tgz#0f4263d1a80c6b2dc7b9c1902c9526478194a988" + integrity sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg== + dependencies: + "@babel/runtime" "^7.1.2" + react-router-dom@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.2.0.tgz#9e65a4d0c45e13289e66c7b17c7e175d0ea15662" @@ -23872,14 +23892,14 @@ reflect.ownkeys@^0.2.0: resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460" integrity sha1-dJrO7H8/34tj+SegSAnpDFwLNGA= -refractor@^3.2.0, refractor@^3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/refractor/-/refractor-3.3.1.tgz#ebbc04b427ea81dc25ad333f7f67a0b5f4f0be3a" - integrity sha512-vaN6R56kLMuBszHSWlwTpcZ8KTMG6aUCok4GrxYDT20UIOXxOc5o6oDc8tNTzSlH3m2sI+Eu9Jo2kVdDcUTWYw== +refractor@^3.2.0, refractor@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/refractor/-/refractor-3.4.0.tgz#62bd274b06c942041f390c371b676eb67cb0a678" + integrity sha512-dBeD02lC5eytm9Gld2Mx0cMcnR+zhSnsTfPpWqFaMgUMJfC9A6bcN3Br/NaXrnBJcuxnLFR90k1jrkaSyV8umg== dependencies: hastscript "^6.0.0" parse-entities "^2.0.0" - prismjs "~1.23.0" + prismjs "~1.24.0" regedit@^3.0.3: version "3.0.3"