diff --git a/docs/user/dashboard/images/lens_areaChartCumulativeNumberOfSalesOnWeekend_7.16.png b/docs/user/dashboard/images/lens_areaChartCumulativeNumberOfSalesOnWeekend_7.16.png new file mode 100644 index 0000000000000..82e0337ffed39 Binary files /dev/null and b/docs/user/dashboard/images/lens_areaChartCumulativeNumberOfSalesOnWeekend_7.16.png differ diff --git a/docs/user/dashboard/images/lens_areaPercentageNumberOfOrdersByCategory_7.16.png b/docs/user/dashboard/images/lens_areaPercentageNumberOfOrdersByCategory_7.16.png new file mode 100644 index 0000000000000..6addc8bc276e9 Binary files /dev/null and b/docs/user/dashboard/images/lens_areaPercentageNumberOfOrdersByCategory_7.16.png differ diff --git a/docs/user/dashboard/images/lens_barChartCustomTimeInterval_7.16.png b/docs/user/dashboard/images/lens_barChartCustomTimeInterval_7.16.png new file mode 100644 index 0000000000000..3aa5484cb6258 Binary files /dev/null and b/docs/user/dashboard/images/lens_barChartCustomTimeInterval_7.16.png differ diff --git a/docs/user/dashboard/images/lens_barChartDistributionOfNumberField_7.16.png b/docs/user/dashboard/images/lens_barChartDistributionOfNumberField_7.16.png new file mode 100644 index 0000000000000..631477e7d68cc Binary files /dev/null and b/docs/user/dashboard/images/lens_barChartDistributionOfNumberField_7.16.png differ diff --git a/docs/user/dashboard/images/lens_clickAndDragZoom_7.16.gif b/docs/user/dashboard/images/lens_clickAndDragZoom_7.16.gif new file mode 100644 index 0000000000000..65fed435dfa25 Binary files /dev/null and b/docs/user/dashboard/images/lens_clickAndDragZoom_7.16.gif differ diff --git a/docs/user/dashboard/images/lens_end_to_end_2_1_1.png b/docs/user/dashboard/images/lens_end_to_end_2_1_1.png index e996b58520d41..f1bee569f29c2 100644 Binary files a/docs/user/dashboard/images/lens_end_to_end_2_1_1.png and b/docs/user/dashboard/images/lens_end_to_end_2_1_1.png differ diff --git a/docs/user/dashboard/images/lens_end_to_end_6_1.png b/docs/user/dashboard/images/lens_end_to_end_6_1.png index 73299bac0354e..942c4d636d1fc 100644 Binary files a/docs/user/dashboard/images/lens_end_to_end_6_1.png and b/docs/user/dashboard/images/lens_end_to_end_6_1.png differ diff --git a/docs/user/dashboard/images/lens_indexPatternDropDown_7.16.png b/docs/user/dashboard/images/lens_indexPatternDropDown_7.16.png new file mode 100644 index 0000000000000..f8e797c7dd4b6 Binary files /dev/null and b/docs/user/dashboard/images/lens_indexPatternDropDown_7.16.png differ diff --git a/docs/user/dashboard/images/lens_index_pattern.png b/docs/user/dashboard/images/lens_index_pattern.png deleted file mode 100644 index 0c89e7ab7f814..0000000000000 Binary files a/docs/user/dashboard/images/lens_index_pattern.png and /dev/null differ diff --git a/docs/user/dashboard/images/lens_layerVisualizationTypeMenu_7.16.png b/docs/user/dashboard/images/lens_layerVisualizationTypeMenu_7.16.png new file mode 100644 index 0000000000000..6ee73e9a67662 Binary files /dev/null and b/docs/user/dashboard/images/lens_layerVisualizationTypeMenu_7.16.png differ diff --git a/docs/user/dashboard/images/lens_leftAxisMenu_7.16.png b/docs/user/dashboard/images/lens_leftAxisMenu_7.16.png new file mode 100644 index 0000000000000..054731adbeef5 Binary files /dev/null and b/docs/user/dashboard/images/lens_leftAxisMenu_7.16.png differ diff --git a/docs/user/dashboard/images/lens_lineChartMetricOverTime_7.16.png b/docs/user/dashboard/images/lens_lineChartMetricOverTime_7.16.png new file mode 100644 index 0000000000000..34fd8dae1407d Binary files /dev/null and b/docs/user/dashboard/images/lens_lineChartMetricOverTime_7.16.png differ diff --git a/docs/user/dashboard/images/lens_lineChartMultipleDataSeries_7.16.png b/docs/user/dashboard/images/lens_lineChartMultipleDataSeries_7.16.png new file mode 100644 index 0000000000000..373fc76b5db41 Binary files /dev/null and b/docs/user/dashboard/images/lens_lineChartMultipleDataSeries_7.16.png differ diff --git a/docs/user/dashboard/images/lens_logsDashboard_7.16.png b/docs/user/dashboard/images/lens_logsDashboard_7.16.png new file mode 100644 index 0000000000000..cdfe0accdbbb5 Binary files /dev/null and b/docs/user/dashboard/images/lens_logsDashboard_7.16.png differ diff --git a/docs/user/dashboard/images/lens_metricUniqueCountOfClientip_7.16.png b/docs/user/dashboard/images/lens_metricUniqueCountOfClientip_7.16.png new file mode 100644 index 0000000000000..bed6acf501a3a Binary files /dev/null and b/docs/user/dashboard/images/lens_metricUniqueCountOfClientip_7.16.png differ diff --git a/docs/user/dashboard/images/lens_metricUniqueVisitors_7.16.png b/docs/user/dashboard/images/lens_metricUniqueVisitors_7.16.png new file mode 100644 index 0000000000000..92fe4fb0676f2 Binary files /dev/null and b/docs/user/dashboard/images/lens_metricUniqueVisitors_7.16.png differ diff --git a/docs/user/dashboard/images/lens_mixedXYChart_7.16.png b/docs/user/dashboard/images/lens_mixedXYChart_7.16.png new file mode 100644 index 0000000000000..76fc96a44a402 Binary files /dev/null and b/docs/user/dashboard/images/lens_mixedXYChart_7.16.png differ diff --git a/docs/user/dashboard/images/lens_pieChartCompareSubsetOfDocs_7.16.png b/docs/user/dashboard/images/lens_pieChartCompareSubsetOfDocs_7.16.png new file mode 100644 index 0000000000000..f8e8ba98f691e Binary files /dev/null and b/docs/user/dashboard/images/lens_pieChartCompareSubsetOfDocs_7.16.png differ diff --git a/docs/user/dashboard/images/lens_referenceLine_7.16.png b/docs/user/dashboard/images/lens_referenceLine_7.16.png new file mode 100644 index 0000000000000..3df7e99e0aafe Binary files /dev/null and b/docs/user/dashboard/images/lens_referenceLine_7.16.png differ diff --git a/docs/user/dashboard/images/lens_tableTopFieldValues_7.16.png b/docs/user/dashboard/images/lens_tableTopFieldValues_7.16.png new file mode 100644 index 0000000000000..64417a9a6392c Binary files /dev/null and b/docs/user/dashboard/images/lens_tableTopFieldValues_7.16.png differ diff --git a/docs/user/dashboard/images/lens_timeSeriesDataTutorialDashboard_7.16.png b/docs/user/dashboard/images/lens_timeSeriesDataTutorialDashboard_7.16.png new file mode 100644 index 0000000000000..bce904c8606ca Binary files /dev/null and b/docs/user/dashboard/images/lens_timeSeriesDataTutorialDashboard_7.16.png differ diff --git a/docs/user/dashboard/images/lens_treemapMultiLevelChart_7.16.png b/docs/user/dashboard/images/lens_treemapMultiLevelChart_7.16.png new file mode 100644 index 0000000000000..6d772a32e9ef4 Binary files /dev/null and b/docs/user/dashboard/images/lens_treemapMultiLevelChart_7.16.png differ diff --git a/docs/user/dashboard/images/lens_visualizationTypeDropdown_7.16.png b/docs/user/dashboard/images/lens_visualizationTypeDropdown_7.16.png new file mode 100644 index 0000000000000..dce53da1f2cad Binary files /dev/null and b/docs/user/dashboard/images/lens_visualizationTypeDropdown_7.16.png differ diff --git a/docs/user/dashboard/lens-advanced.asciidoc b/docs/user/dashboard/lens-advanced.asciidoc index 02e0afd2c0311..324676ecb0a8e 100644 --- a/docs/user/dashboard/lens-advanced.asciidoc +++ b/docs/user/dashboard/lens-advanced.asciidoc @@ -2,18 +2,21 @@ == Analyze time series data In this tutorial, you'll use the ecommerce sample data to analyze sales trends, but you can use any type of data to complete the tutorial. -Before using this tutorial, review the <>. + +When you're done, you'll have a complete overview of the sample web logs data. [role="screenshot"] -image::images/final_time_series_analysis_dashboard.png[Final dashboard with ecommerce sample data, width=50%] +image::images/lens_timeSeriesDataTutorialDashboard_7.16.png[Final dashboard with ecommerce sample data] + +Before you begin, you should be familiar with the <>. [discrete] [[add-the-data-and-create-the-dashboard-advanced]] === Add the data and create the dashboard -Add the sample ecommerce data that you'll use to create the dashboard panels. +Add the sample ecommerce data, and create and set up the dashboard. -. Go to the {kib} *Home* page, then click *Try our sample data*. +. Go to the *Home* page, then click *Try sample data*. . On the *Sample eCommerce orders* card, click *Add data*. @@ -25,40 +28,30 @@ Create the dashboard where you'll display the visualization panels. [float] [[open-and-set-up-lens-advanced]] -=== Open and set up Lens +=== Open and set up the visualization editor -Open *Lens*, then make sure the correct fields appear. +Open the visualization editor, then make sure the correct fields appear. -. From the dashboard, click *Create visualization*. +. On the dashboard, click *Create visualization*. -. Make sure the *kibana_sample_data_ecommerce* index appears. -+ -If you are using your own data, select the <> that contains your data. +. Make sure the *kibana_sample_data_ecommerce* index appears, then set the <> to *Last 30 days*. [discrete] [[custom-time-interval]] -=== View a date histogram with a custom time interval - -It is common to use the automatic date histogram interval, but sometimes you want a larger or smaller -interval. For performance reasonse, *Lens* lets you choose the minimum time interval, not the exact time interval. The performance limit is controlled by the <> setting and the <>. +=== Create visualizations with custom time intervals -If you are using your own data, use one of the following options to see hourly sales over the last 30 days: +When you create visualizations with time series data, you can use the default time interval, or increase and decrease the interval. For performance reasons, the visualization editor allows you to choose the minimum time interval, but not the exact time interval. The interval limit is controlled by the <> setting and <>. -* View less than 30 days at a time, then use the time filter to select each day separately. - -* Increase `histogram:maxBars` to at least 720, which is the number of hours in 30 days. This affects all visualizations and can reduce performance. - -If you are using the sample data, use *Normalize unit*, which converts *Average sales per 12 hours* -into *Average sales per 12 hours (per hour)* by dividing the number of hours: - -. Set the <> to *Last 30 days*. +To analyze the data with a custom time interval, create a bar chart that shows you how many orders were made at your store every hour: . From the *Available fields* list, drag *Records* to the workspace. ++ +The visualization editor creates a bar chart. -. To zoom in on the data you want to view, click and drag your cursor across the bars. +. To zoom in on the data, click and drag your cursor across the bars. + [role="screenshot"] -image::images/lens_advanced_1_1.png[Added records to the workspace] +image::images/lens_clickAndDragZoom_7.16.gif[Cursor clicking and dragging across the bars to zoom in on the data] . In the layer pane, click *Count of Records*. @@ -67,32 +60,51 @@ image::images/lens_advanced_1_1.png[Added records to the workspace] .. Click *Add advanced options > Normalize by unit*. .. From the *Normalize by unit* dropdown, select *per hour*, then click *Close*. ++ +*Normalize unit* converts *Average sales per 12 hours* into *Average sales per 12 hours (per hour)* by dividing the number of hours. . To hide the *Horizontal axis* label, open the *Bottom Axis* menu, then deselect *Show*. -+ -You have a bar chart that shows you how many orders were made at your store every hour. + +To identify the 75th percentile of orders, add a reference line: + +. In the layer pane, click *Add layer > Add reference layer*. + +. Click *Static value*. + +. Click the *Percentile* function, then enter `75` in the *Percentile* field. + +. Configure the display options. + +.. In the *Display name* field, enter `75th`. + +.. Select *Show display name*. + +.. From the *Icon* dropdown, select *Tag*. + +.. In the *Color* field, enter `#E7664C`. + +. Click *Close*. + [role="screenshot"] -image::images/lens_advanced_1_2.png[Orders per day] +image::images/lens_barChartCustomTimeInterval_7.16.png[Orders per day] . Click *Save and return*. [discrete] [[add-a-data-layer-advanced]] -=== Monitor multiple series +=== Analyze multiple data series -It is often required to monitor multiple series within a time interval. These series can have similar configurations with minor differences. -*Lens* copies a function when you drag it to the *Drop a field or click to add* field within the same group. +You can create visualizations with multiple data series within the same time interval, even when the series have similar configurations with minor differences. -To quickly create many copies of a percentile metric that shows distribution of price over time: +To analyze multiple series, create a line chart that displays the price distribution of products sold over time: . On the dashboard, click *Create visualization*. -. Open the *Chart Type* dropdown, then select *Line*. +. Open the *Visualization type* dropdown, then select *Line*. . From the *Available fields* list, drag *products.price* to the workspace. -Create the 95th percentile. +Create the 95th price distribution percentile: . In the layer pane, click *Median of products.price*. @@ -100,9 +112,9 @@ Create the 95th percentile. . In the *Display name* field, enter `95th`, then click *Close*. -To create the 90th percentile, duplicate the `95th` percentile. +To copy a function, you drag it to the *Drop a field or click to add* field within the same group. To create the 90th percentile, duplicate the `95th` percentile: -. Drag the *95th* field to the *Drop a field or click to add* field in the *Vertical axis* group. +. Drag the *95th* field to *Add or drag-and-drop a field* for *Vertical axis*. + [role="screenshot"] image::images/lens_advanced_2_2.gif[Easily duplicate the items with drag and drop] @@ -111,22 +123,22 @@ image::images/lens_advanced_2_2.gif[Easily duplicate the items with drag and dro . In the *Display name* field enter `90th`, then click *Close*. -. Repeat the duplication steps to create the `50th` and `10th` percentiles. +. To create the `50th` and `10th` percentiles, repeat the duplication steps. . Open the *Left Axis* menu, then enter `Percentiles for product prices` in the *Axis name* field. + -You have a line chart that shows you the price distribution of products sold over time. -+ [role="screenshot"] -image::images/lens_advanced_2_3.png[Percentiles for product prices chart] +image::images/lens_lineChartMultipleDataSeries_7.16.png[Percentiles for product prices chart] . Click *Save and return*. [discrete] [[add-a-data-layer]] -==== Add multiple chart types or index patterns +=== Analyze multiple visualization types + +With layers, you can analyze your data with multiple visualization types. When you create layered visualizations, match the data on the horizontal axis so that it uses the same scale. -To overlay visualization types or index patterns, add layers. When you create layered charts, match the data on the horizontal axis so that it uses the same scale. +To analyze multiple visualization types, create an area chart that displays the average order prices, then add a line chart layer that displays the number of customers. . On the dashboard, click *Create visualization*. @@ -136,19 +148,19 @@ To overlay visualization types or index patterns, add layers. When you create la .. Click the *Average* function. -.. In the *Display name* field, enter `Average of prices`, then click *Close*. +.. In the *Display name* field, enter `Average price`, then click *Close*. -. Open the *Chart Type* dropdown, then select *Area*. +. Open the *Visualization type* dropdown, then select *Area*. -Create a new layer to overlay with custom traffic. +Add a layer to display the customer traffic: -. In the layer pane, click *+*. +. In the layer pane, click *Add layer > Add visualization layer*. . From the *Available fields* list, drag *customer_id* to the *Vertical Axis* field in the second layer. -. In the second layer, click *Unique count of customer_id*. +. In the layer pane, click *Unique count of customer_id*. -.. In the *Display name* field, enter `Unique customers`. +.. In the *Display name* field, enter `Number of customers`. .. In the *Series color* field, enter *#D36086*. @@ -156,12 +168,15 @@ Create a new layer to overlay with custom traffic. . From the *Available fields* list, drag *order_date* to the *Horizontal Axis* field in the second layer. -. In the second layer pane, open the *Chart type* menu, then click the line chart. +. In the second layer, open the *Layer visualization type* menu, then click *Line*. + [role="screenshot"] -image::images/lens_advanced_3_2.png[Change layer type] +image::images/lens_layerVisualizationTypeMenu_7.16.png[Layer visualization type menu] -. Open the *Legend* menu, then select the arrow that points up. +. To change the position of the legend, open the *Legend* menu, then select the *Alignment* arrow that points up. ++ +[role="screenshot"] +image::images/lens_mixedXYChart_7.16.png[Layer visualization type menu] . Click *Save and return*. @@ -169,35 +184,35 @@ image::images/lens_advanced_3_2.png[Change layer type] [[percentage-stacked-area]] === Compare the change in percentage over time -By default, *Lens* shows *date histograms* using a stacked chart visualization, which helps understand how distinct sets of documents perform over time. Sometimes it is useful to understand how the distributions of these sets change over time. -Combine *filters* and *date histogram* functions to see the change over time in specific -sets of documents. To view this as a percentage, use a *Stacked percentage* bar or area chart. +By default, the visualization editor displays time series data with stacked charts, which show how the different document sets change over time. + +To view change over time as a percentage, create an *Area percentage* chart that displays three order categories over time: . On the dashboard, click *Create visualization*. . From the *Available fields* list, drag *Records* to the workspace. -. Open the *Chart type* dropdown, then select *Area percentage*. +. Open the *Visualization type* dropdown, then select *Area percentage*. -For each category type, create a filter. +For each order category, create a filter: -. In the layer pane, click the *Drop a field or click to add* field for *Break down by*. +. In the layer pane, click *Add or drag-and-drop a field* for *Break down by*. . Click the *Filters* function. -. Click *All records*, enter the following, then press Return: +. Click *All records*, enter the following in the query bar, then press Return: * *KQL* — `category.keyword : *Clothing` * *Label* — `Clothing` -. Click *Add a filter*, enter the following, then press Return: +. Click *Add a filter*, enter the following in the query bar, then press Return: * *KQL* — `category.keyword : *Shoes` * *Label* — `Shoes` -. Click *Add a filter*, enter the following, then press Return: +. Click *Add a filter*, enter the following in the query bar, then press Return: * *KQL* — `category.keyword : *Accessories` @@ -205,10 +220,10 @@ For each category type, create a filter. . Click *Close*. -. Open the *Legend* menu, then select the arrow that points up. +. Open the *Legend* menu, then select the *Alignment* arrow that points up. + [role="screenshot"] -image::images/lens_advanced_4_1.png[Prices share by category] +image::images/lens_areaPercentageNumberOfOrdersByCategory_7.16.png[Prices share by category] . Click *Save and return*. @@ -220,9 +235,9 @@ To determine the number of orders made only on Saturday and Sunday, create an ar . On the dashboard, click *Create visualization*. -. Open the *Chart Type* dropdown, then select *Area*. +. Open the *Visualization type* dropdown, then select *Area*. -Configure the cumulative sum of the store orders. +Configure the cumulative sum of store orders: . From the *Available fields* list, drag *Records* to the workspace. @@ -230,15 +245,15 @@ Configure the cumulative sum of the store orders. . Click the *Cumulative sum* function. -. In the *Display name* field, enter `Cumulative orders during weekend days`, then click *Close*. +. In the *Display name* field, enter `Cumulative weekend orders`, then click *Close*. -Filter the results to display the data for only Saturday and Sunday. +Filter the results to display the data for only Saturday and Sunday: -. In the layer pane, click the *Drop a field or click to add* field for *Break down by*. +. In the layer pane, click *Add or drag-and-drop a field* for *Break down by*. . Click the *Filters* function. -. Click *All records*, enter the following, then press Return: +. Click *All records*, enter the following in the query bar, then press Return: * *KQL* — `day_of_week : "Saturday" or day_of_week : "Sunday"` @@ -249,7 +264,7 @@ The <> displays all documents where `day_of_week` matche . Open the *Legend* menu, then click *Hide*. + [role="screenshot"] -image::images/lens_advanced_5_2.png[Line chart with cumulative sum of orders made on the weekend] +image::images/lens_areaChartCumulativeNumberOfSalesOnWeekend_7.16.png[Area chart with cumulative sum of orders made on the weekend] . Click *Save and return*. @@ -257,30 +272,25 @@ image::images/lens_advanced_5_2.png[Line chart with cumulative sum of orders mad [[compare-time-ranges]] === Compare time ranges -*Lens* allows you to compare the selected time range with historical data using the *Time shift* option. +With *Time shift*, you can compare the data from different time ranges. To make sure the data correctly displays, choose a multiple of the date histogram interval when you use multiple time shifts. For example, you are unable to use a *36h* time shift for one series, and a *1d* time shift for the second series if the interval is *days*. -If multiple time shifts are used in a single chart, a multiple of the date histogram interval should be chosen, or the data points might not line up and gaps can appear. -For example, if a daily interval is used, shifting one series by *36h*, and another by *1d* is not recommended. You can reduce the interval to *12h*, or create two separate charts. - -To compare current sales numbers with sales from a week ago, follow these steps: +To compare two time ranges, create a line chart that compares the sales in the current week with sales from the previous week: . On the dashboard, click *Create visualization*. -. Open the *Chart Type* dropdown, then select *Line*. +. Open the *Visualization type* dropdown, then select *Line*. . From the *Available fields* list, drag *Records* to the workspace. -. In the layer pane, drag *Count of Records* to the *Drop a field or click to add* field in the *Vertical axis* group. +. To duplicate *Count of Records*, drag *Count of Records* to *Add or drag-and-drop a field* for *Vertical axis* in the layer pane. -To create a week-over-week comparison, shift the second *Count of Records* by one week. +To create a week-over-week comparison, shift *Count of Records [1]* by one week: . In the layer pane, click *Count of Records [1]*. -. Open the *Add advanced options* dropdown, then select *Time shift*. - -. Click *1 week ago*. +. Click *Add advanced options > Time shift*, select *1 week ago*, then click *Close*. + -To define custom time shifts, enter the time value, the time increment, then press Enter. For example, to use a one week time shift, enter *1w*. +To use custom time shifts, enter the time value and increment, then press Enter. For example, enter *1w* to use the *1 week ago* time shift. + [role="screenshot"] image::images/lens_time_shift.png[Line chart with week-over-week sales comparison] @@ -289,9 +299,11 @@ image::images/lens_time_shift.png[Line chart with week-over-week sales compariso [float] [[compare-time-as-percent]] -==== Compare time ranges as a percent change +==== Analyze the percent change between time ranges -To view the percent change in sales between the current time and the previous week, create a *Formula*. +With *Formula*, you can analyze the percent change in your data from different time ranges. + +To compare time range changes as a percent, create a bar chart that compares the sales in the current week with sales from the previous week: . On the dashboard, click *Create visualization*. @@ -299,11 +311,11 @@ To view the percent change in sales between the current time and the previous we . In the layer pane, click *Count of Records*. -.. Click *Formula*, then enter `count() / count(shift='1w') - 1`. +. Click *Formula*, then enter `count() / count(shift='1w') - 1`. -.. Open the *Value format* dropdown, select *Percent*, then enter `0` in the *D*ecimals* field. +. Open the *Value format* dropdown, select *Percent*, then enter `0` in the *Decimals* field. -.. In the *Display name* field, enter `Percent change`, then click *Close*. +. In the *Display name* field, enter `Percent of change`, then click *Close*. + [role="screenshot"] image::images/lens_percent_chage.png[Bar chart with percent change in sales between the current time and the previous week] @@ -312,34 +324,33 @@ image::images/lens_percent_chage.png[Bar chart with percent change in sales betw [discrete] [[view-customers-over-time-by-continents]] -=== Create a table of customers by category over time +=== Analyze the data in a table -Tables are useful when you want to display the actual field values. -You can build a date histogram table, and group the customer count metric by category, such as the continent registered in user accounts. +With tables, you can view and compare the field values, which is useful for displaying the locations of customer orders. -In *Lens* you can split the metric in a table leveraging the *Columns* field, where each data value from the aggregation is used as column of the table and the relative metric value is shown. +Create a date histogram table and group the customer count metric by category, such as the continent registered in user accounts: . On the dashboard, click *Create visualization*. -. Open the *Chart Type* dropdown, then click *Table*. +. Open the *Visualization type* dropdown, then select *Table*. . From the *Available fields* list, drag *customer_id* to the *Metrics* field in the layer pane. -. In the layer pane, click *Unique count of customer_id*. +.. In the layer pane, click *Unique count of customer_id*. -. In the *Display name* field, enter `Customers`, then click *Close*. +.. In the *Display name* field, enter `Customers`, then click *Close*. . From the *Available fields* list, drag *order_date* to the *Rows* field in the layer pane. -. In the layer pane, click the *order_date*. +.. In the layer pane, click the *order_date*. .. Select *Customize time interval*. .. Change the *Minimum interval* to *1 days*. -.. In the *Display name* field, enter `Sale`, then click *Close*. +.. In the *Display name* field, enter `Sales`, then click *Close*. -Add columns for each continent. +To split the metric, add columns for each continent using the *Columns* field: . From the *Available fields* list, drag *geoip.continent_name* to the *Columns* field in the layer pane. + @@ -360,3 +371,6 @@ Now that you have a complete overview of your ecommerce sales data, save the das . Select *Store time with dashboard*. . Click *Save*. + +[role="screenshot"] +image::images/lens_timeSeriesDataTutorialDashboard_7.16.png[Final dashboard with ecommerce sample data] diff --git a/docs/user/dashboard/lens.asciidoc b/docs/user/dashboard/lens.asciidoc index c3e0a5523a78d..23a6d1fbcfd3d 100644 --- a/docs/user/dashboard/lens.asciidoc +++ b/docs/user/dashboard/lens.asciidoc @@ -48,6 +48,8 @@ Choose the data you want to visualize. . If you want to learn more about the data a field contains, click the field. +. To visualize more than one index pattern, click *Add layer > Add visualization layer*, then select the index pattern. + Edit and delete. . To change the aggregation *Quick function* and display options, click the field in the layer pane. @@ -60,11 +62,11 @@ Edit and delete. Change the fields list to display a different index pattern, different time range, or add your own fields. -* To create a visualization with fields in a different index pattern, open the *Change index pattern* dropdown, then select the index pattern. +* To create a visualization with fields in a different index pattern, open the *Index pattern* dropdown, then select the index pattern. * If the fields list is empty, change the <>. -* To add fields, open the action menu (*...*) next to the *Change index pattern* dropdown, then select *Add field to index pattern*. +* To add fields, open the action menu (*...*) next to the *Index pattern* dropdown, then select *Add field to index pattern*. + [role="screenshot"] image:images/runtime-field-menu.png[Dropdown menu located next to index pattern field with items for adding and managing fields, width=50%] @@ -176,6 +178,29 @@ Compare your real-time data set to the results that are offset by a time increme For a time shift example, refer to <>. +[float] +[[add-reference-lines]] +==== Add reference lines + +With reference lines, you can identify specific values in your visualizations with icons, colors, and other display options. You can add reference lines to any visualization type that displays axes. + +For example, to track the number of bytes in the 75th percentile, add a shaded *Percentile* reference line to your time series visualization. + +[role="screenshot"] +image::images/lens_referenceLine_7.16.png[Lens drag and drop focus state] + +. In the layer pane, click *Add layer > Add reference layer*. + +. Click the reference line value, then specify the reference line you want to use: + +* To add a static reference line, click *Static*, then enter the reference line value you want to use. + +* To add a dynamic reference line, click *Quick functions*, then click and configure the functions you want to use. + +* To calculate the reference line value with math, click *Formula*, then enter the formula. + +. Specify the display options, such as *Display name* and *Icon*, then click *Close*. + [float] [[filter-the-data]] ==== Apply filters @@ -236,9 +261,29 @@ The following component menus are available: * *Left axis*, *Bottom axis*, and *Right axis* — Specify how you want to display the chart axes. For example, add axis labels and change the orientation and bounds. +[float] +[[view-data-and-requests]] +==== View the visualization data and requests + +To view the data included in the visualization and the requests that collected the data, use the *Inspector*. + +. In the toolbar, click *Inspect*. + +. Open the *View* dropdown, then click *Data*. + +.. From the dropdown, select the table that contains the data you want to view. + +.. To download the data, click *Download CSV*, then select the format type. + +. Open the *View* dropdown, then click *Requests*. + +.. From the dropdown, select the requests you want to view. + +.. To view the requests in *Console*, click *Request*, then click *Open in Console*. + [float] [[save-the-lens-panel]] -===== Save and add the panel +==== Save and add the panel Save the panel to the *Visualize Library* and add it to the dashboard, or add it to the dashboard without saving. @@ -408,7 +453,7 @@ To configure the bounds, use the menus in the editor toolbar. Bar and area chart .*Is it possible to display icons in data tables?* [%collapsible] ==== -You can display icons with <> in data tables. +You can display icons with <> in data tables. ==== [discrete] diff --git a/docs/user/dashboard/tutorial-create-a-dashboard-of-lens-panels.asciidoc b/docs/user/dashboard/tutorial-create-a-dashboard-of-lens-panels.asciidoc index c3d76ee88322b..e270c16cf60f6 100644 --- a/docs/user/dashboard/tutorial-create-a-dashboard-of-lens-panels.asciidoc +++ b/docs/user/dashboard/tutorial-create-a-dashboard-of-lens-panels.asciidoc @@ -1,21 +1,24 @@ [[create-a-dashboard-of-panels-with-web-server-data]] -== Build your first dashboard +== Create your first dashboard -Learn the most common ways to build a dashboard from your own data. +Learn the most common ways to create a dashboard from your own data. The tutorial will use sample data from the perspective of an analyst looking at website logs, but this type of dashboard works on any type of data. -Before using this tutorial, you should be familiar with the <>. + +When you're done, you'll have a complete overview of the sample web logs data. [role="screenshot"] -image::images/lens_end_to_end_dashboard.png[Final dashboard vis] +image::images/lens_logsDashboard_7.16.png[Logs dashboard] + +Before you begin, you should be familiar with the <>. [discrete] [[add-the-data-and-create-the-dashboard]] === Add the data and create the dashboard -Add the sample web logs data that you'll use to create the dashboard panels. +Add the sample web logs data, and create and set up the dashboard. -. Go to the {kib} *Home* page, then click *Try our sample data*. +. Go to the *Home* page, then click *Try sample data*. . On the *Sample web logs* card, click *Add data*. @@ -29,56 +32,70 @@ Create the dashboard where you'll display the visualization panels. [float] [[open-and-set-up-lens]] -=== Open Lens and get familiar with the data +=== Open the visualization editor and get familiar with the data + +Open the visualization editor, then make sure the correct fields appear. . On the dashboard, click *Create visualization*. . Make sure the *kibana_sample_data_logs* index appears. + [role="screenshot"] -image::images/lens_end_to_end_1_2.png[Lens index pattern selector, width=50%] +image::images/lens_indexPatternDropDown_7.16.png[Index pattern dropdown] + +To create the visualizations in this tutorial, you'll use the following fields: + +* *Records* -. To create the visualizations in this tutorial, you'll use the *Records*, *timestamp*, *bytes*, *clientip*, and *referer.keyword* fields. To see the most frequent values of a field, hover over the field name, then click *i*. +* *timestamp* + +* *bytes* + +* *clientip* + +* *referer.keyword* + +To see the most frequent values in a field, hover over the field name, then click *i*. [discrete] [[view-the-number-of-website-visitors]] === Create your first visualization -Pick a field you want to analyze, such as *clientip*. If you want -to analyze only this field, you can use the *Metric* visualization to display a big number. -The only number function that you can use with *clientip* is *Unique count*. -*Unique count*, also referred to as cardinality, approximates the number of unique values -of the *clientip* field. +Pick a field you want to analyze, such as *clientip*. To analyze only the *clientip* field, use the *Metric* visualization to display the field as a number. + +The only number function that you can use with *clientip* is *Unique count*, also referred to as cardinality, which approximates the number of unique values. -. To select the visualization type, open the *Chart type* dropdown, then select *Metric*. +. Open the *Visualization type* dropdown, then select *Metric*. + [role="screenshot"] -image::images/lens_end_to_end_1_2_1.png[Chart Type dropdown with Metric selected, width=50%] +image::images/lens_visualizationTypeDropdown_7.16.png[Visualization type dropdown] -. From the *Available fields* list, drag *clientip* to the workspace. +. From the *Available fields* list, drag *clientip* to the workspace or layer pane. + [role="screenshot"] -image::images/lens_end_to_end_1_3.png[Changed type and dropped clientip field] +image::images/lens_metricUniqueCountOfClientip_7.16.png[Metric visualization of the clientip field] + -*Lens* selects the *Unique count* function because it is the only numeric function -that works for IP addresses. You can also drag *clientip* to the layer pane for the same result. +In the layer pane, *Unique count of clientip* appears because the editor automatically applies the *Unique count* function to the *clientip* field. *Unique count* is the only numeric function that works with IP addresses. . In the layer pane, click *Unique count of clientip*. .. In the *Display name* field, enter `Unique visitors`. .. Click *Close*. ++ +[role="screenshot"] +image::images/lens_metricUniqueVisitors_7.16.png[Metric visualization that displays number of unique visitors] . Click *Save and return*. + -The metric visualization has its own label, so you do not need to add a panel title. +*[No Title]* appears in the visualization panel header. Since the visualization has its own `Unique visitors` label, you do not need to add a panel title. [discrete] [[mixed-multiaxis]] === View a metric over time -*Lens* has two shortcuts that simplify viewing metrics over time. -If you drag a numeric field to the workspace, *Lens* adds the default +There are two shortcuts you can use to view metrics over time. +When you drag a numeric field to the workspace, the visualization editor adds the default time field from the index pattern. When you use the *Date histogram* function, you can replace the time field by dragging the field to the workspace. @@ -88,78 +105,76 @@ To visualize the *bytes* field over time: . From the *Available fields* list, drag *bytes* to the workspace. + -*Lens* creates a bar chart with the *timestamp* and *Median of bytes* fields, and automatically chooses a date interval. +The visualization editor creates a bar chart with the *timestamp* and *Median of bytes* fields. -. To zoom in on the data you want to view, click and drag your cursor across the bars. +. To zoom in on the data, click and drag your cursor across the bars. + [role="screenshot"] image::images/lens_end_to_end_3_1_1.gif[Zoom in on the data] -To emphasize the change in *Median of bytes* over time, change to a line chart with one of the following options: - -* From the *Suggestions*, click the line chart. -* Open the *Chart type* dropdown in the editor toolbar, then select *Line*. -* Open the *Chart type* menu in the layer pane, then click the line chart. +To emphasize the change in *Median of bytes* over time, change the visualization type to *Line* with one of the following options: -You can increase and decrease the minimum interval that *Lens* uses, but you are unable to decrease the interval -below the <>. +* In the *Suggestions*, click the line chart. +* In the editor toolbar, open the *Visualization type* dropdown, then select *Line*. +* In the layer pane, open the *Layer visualization type* menu, then click *Line*. -To set the minimum time interval: +To increase the minimum time interval: . In the layer pane, click *timestamp*. . Select *Customize time interval*. . Change the *Minimum interval* to *1 days*, then click *Close*. ++ +You can increase and decrease the minimum interval, but you are unable to decrease the interval below the <>. -To save space on the dashboard, hide the vertical and horizontal axis labels. +To save space on the dashboard, hide the axis labels. . Open the *Left axis* menu, then deselect *Show*. + [role="screenshot"] -image::images/lens_end_to_end_4_3.png[Turn off axis label] +image::images/lens_leftAxisMenu_7.16.png[Left axis menu] . Open the *Bottom axis* menu, then deselect *Show*. ++ +[role="screenshot"] +image::images/lens_lineChartMetricOverTime_7.16.png[Line chart that displays metric data over time] . Click *Save and return* -Add a panel title to explain the panel, which is necessary because you removed the axis labels. +Since you removed the axis labels, add a panel title: -.. Open the panel menu, then select *Edit panel title*. +. Open the panel menu, then select *Edit panel title*. -.. In the *Panel title* field, enter `Median of bytes`, then click *Save*. +. In the *Panel title* field, enter `Median of bytes`, then click *Save*. [discrete] [[view-the-distribution-of-visitors-by-operating-system]] === View the top values of a field +Create a visualization that displays the most frequent values of *request.keyword* on your website, ranked by the unique visitors. +To create the visualization, use *Top values of request.keyword* ranked by *Unique count of clientip*, instead of being ranked by *Count of records*. + The *Top values* function ranks the unique values of a field by another function. The values are the most frequent when ranked by a *Count* function, and the largest when ranked by the *Sum* function. -Create a visualization that displays the most frequent values of *request.keyword* on your website, ranked by the unique visitors. -To create the visualization, use *Top values of request.keyword* ranked by *Unique count of clientip*, instead of -being ranked by *Count of records*. - . On the dashboard, click *Create visualization*. . From the *Available fields* list, drag *clientip* to the *Vertical axis* field in the layer pane. + -*Lens* automatically chooses the *Unique count* function. If you drag *clientip* to the workspace, *Lens* adds the field to the incorrect axis. -+ -When you drag a text or IP address field to the workspace, -*Lens* adds the *Top values* function ranked by *Count of records* to show the most frequent values. +The visualization editor automatically applies the *Unique count* function. If you drag *clientip* to the workspace, the editor adds the field to the incorrect axis. . Drag *request.keyword* to the workspace. + [role="screenshot"] image::images/lens_end_to_end_2_1_1.png[Vertical bar chart with top values of request.keyword by most unique visitors] + -*Lens* adds *Top values of request.keyword* to the *Horizontal axis*. +When you drag a text or IP address field to the workspace, +the editor adds the *Top values* function ranked by *Count of records* to show the most frequent values. -The chart is hard to read because the *request.keyword* field contains long text. You could try -using one of the *Suggestions*, but the suggestions also have issues with long text. Instead, create a *Table* visualization. +The chart labels are unable to display because the *request.keyword* field contains long text fields. You could use one of the *Suggestions*, but the suggestions also have issues with long text. The best way to display long text fields is with the *Table* visualization. -. Open the *Chart type* dropdown, then select *Table*. +. Open the *Visualization type* dropdown, then select *Table*. + [role="screenshot"] image::images/lens_end_to_end_2_1_2.png[Table with top values of request.keyword by most unique visitors] @@ -171,16 +186,19 @@ image::images/lens_end_to_end_2_1_2.png[Table with top values of request.keyword .. In the *Display name* field, enter `Page URL`. .. Click *Close*. ++ +[role="screenshot"] +image::images/lens_tableTopFieldValues_7.16.png[Table that displays the top field values] . Click *Save and return*. + -The table does not need a panel title because the columns are clearly labeled. +Since the table columns are labeled, you do not need to add a panel title. [discrete] [[custom-ranges]] === Compare a subset of documents to all documents -Create a proportional visualization that helps you to determine if your users transfer more bytes from documents under 10KB versus documents over 10 Kb. +Create a proportional visualization that helps you determine if your users transfer more bytes from documents under 10KB versus documents over 10Kb. . On the dashboard, click *Create visualization*. @@ -190,12 +208,14 @@ Create a proportional visualization that helps you to determine if your users tr . From the *Available fields* list, drag *bytes* to the *Break down by* field in the layer pane. -Use the *Intervals* function to select documents based on the number range of a field. -If the ranges were non numeric, or if the query required multiple clauses, you could use the *Filters* function. +To select documents based on the number range of a field, use the *Intervals* function. +When the ranges are non numeric, or the query requires multiple clauses, you could use the *Filters* function. + +Specify the file size ranges: -. To specify the file size ranges, click *bytes* in the layer pane. +. In the layer pane, click *bytes*. -. Click *Create custom ranges*, enter the following, then press Return: +. Click *Create custom ranges*, enter the following in the *Ranges* field, then press Return: * *Ranges* — `0` -> `10240` @@ -214,27 +234,30 @@ image::images/lens_end_to_end_6_1.png[Custom ranges configuration] To display the values as a percentage of the sum of all values, use the *Pie* chart. -. Open the *Chart Type* dropdown, then select *Pie*. +. Open the *Visualization Type* dropdown, then select *Pie*. ++ +[role="screenshot"] +image::images/lens_pieChartCompareSubsetOfDocs_7.16.png[Pie chart that compares a subset of documents to all documents] . Click *Save and return*. -. Add a panel title. +Add a panel title: -.. Open the panel menu, then select *Edit panel title*. +. Open the panel menu, then select *Edit panel title*. -.. In the *Panel title* field, enter `Sum of bytes from large requests`, then click *Save*. +. In the *Panel title* field, enter `Sum of bytes from large requests`, then click *Save*. [discrete] [[histogram]] === View the distribution of a number field -Knowing the distribution of a number helps you find patterns. For example, you can analyze the website traffic per hour to find the best time to do routine maintenance. +The distribution of a number can help you find patterns. For example, you can analyze the website traffic per hour to find the best time for routine maintenance. . On the dashboard, click *Create visualization*. . From the *Available fields* list, drag *bytes* to *Vertical axis* field in the layer pane. -. In the layer pane, click *Median of bytes* +. In the layer pane, click *Median of bytes*. .. Click the *Sum* function. @@ -246,70 +269,80 @@ Knowing the distribution of a number helps you find patterns. For example, you c . In the layer pane, click *hour_of_day*, then slide the *Intervals granularity* slider until the horizontal axis displays hourly intervals. + -The *Intervals* function displays an evenly spaced distribution of the field. +[role="screenshot"] +image::images/lens_barChartDistributionOfNumberField_7.16.png[Bar chart that displays the distribution of a number field] . Click *Save and return*. +Add a panel title: + +. Open the panel menu, then select *Edit panel title*. + +. In the *Panel title* field, enter `Website traffic`, then click *Save*. + [discrete] [[treemap]] === Create a multi-level chart -You can use multiple functions in data tables and proportion charts. For example, -to create a chart that breaks down the traffic sources and user geography, use *Filters* and -*Top values*. +*Table* and *Proportion* visualizations support multiple functions. For example, to create visualizations that break down the data by website traffic sources and user geography, apply the *Filters* and *Top values* functions. . On the dashboard, click *Create visualization*. -. Open the *Chart type* dropdown, then select *Treemap*. +. Open the *Visualization type* dropdown, then select *Treemap*. . From the *Available fields* list, drag *Records* to the *Size by* field in the layer pane. -. In the editor, click the *Drop a field or click to add* field for *Group by*, then create a filter for each website traffic source. +. In the editor, click *Add or drag-and-drop a field* for *Group by*. -.. From *Select a function*, click *Filters*. +Create a filter for each website traffic source: -.. Click *All records*, enter the following, then press Return: +. From *Select a function*, click *Filters*. + +. Click *All records*, enter the following in the query bar, then press Return: * *KQL* — `referer : *facebook.com*` * *Label* — `Facebook` -.. Click *Add a filter*, enter the following, then press Return: +. Click *Add a filter*, enter the following in the query bar, then press Return: * *KQL* — `referer : *twitter.com*` * *Label* — `Twitter` -.. Click *Add a filter*, enter the following, then press Return: +. Click *Add a filter*, enter the following in the query bar, then press Return: * *KQL* — `NOT referer : *twitter.com* OR NOT referer: *facebook.com*` * *Label* — `Other` -.. Click *Close*. +. Click *Close*. -Add a geography grouping: +Add the user geography grouping: -. From the *Available fields* list, drag *geo.src* to the workspace. +. From the *Available fields* list, drag *geo.srcdest* to the workspace. -. To change the *Group by* order, drag *Top values of geo.src* so that it appears first. +. To change the *Group by* order, drag *Top values of geo.srcdest* in the layer pane so that appears first. + [role="screenshot"] image::images/lens_end_to_end_7_2.png[Treemap visualization] -. To view only the Facebook and Twitter data, remove the *Other* category. +Remove the documents that do not match the filter criteria: -.. In the layer pane, click *Top values of geo.src*. +. In the layer pane, click *Top values of geo.srcdest*. -.. Open the *Advanced* dropdown, deselect *Group other values as "Other"*, then click *Close*. +. Click *Advanced*, then deselect *Group other values as "Other"*, the click *Close*. ++ +[role="screenshot"] +image::images/lens_treemapMultiLevelChart_7.16.png[Treemap visualization] . Click *Save and return*. -. Add a panel title. +Add a panel title: -.. Open the panel menu, then select *Edit panel title*. +. Open the panel menu, then select *Edit panel title*. -.. In the *Panel title* field, enter `Page views by location and referrer`, then click *Save*. +. In the *Panel title* field, enter `Page views by location and referrer`, then click *Save*. [float] [[arrange-the-lens-panels]] @@ -317,7 +350,7 @@ image::images/lens_end_to_end_7_2.png[Treemap visualization] Resize and move the panels so they all appear on the dashboard without scrolling. -Decrease the size of the following panels, then move them to the first row: +Decrease the size of the following panels, then move the panels to the first row: * *Unique visitors* @@ -325,7 +358,10 @@ Decrease the size of the following panels, then move them to the first row: * *Sum of bytes from large requests* -* *hour_of_day* +* *Website traffic* ++ +[role="screenshot"] +image::images/lens_logsDashboard_7.16.png[Logs dashboard] [discrete] === Save the dashboard diff --git a/packages/elastic-apm-synthtrace/src/lib/utils/clean_write_targets.ts b/packages/elastic-apm-synthtrace/src/lib/utils/clean_write_targets.ts index 3c514e1097b31..4a2ab281a2849 100644 --- a/packages/elastic-apm-synthtrace/src/lib/utils/clean_write_targets.ts +++ b/packages/elastic-apm-synthtrace/src/lib/utils/clean_write_targets.ts @@ -27,6 +27,7 @@ export async function cleanWriteTargets({ index: targets, allow_no_indices: true, conflicts: 'proceed', + refresh: true, body: { query: { match_all: {}, diff --git a/packages/kbn-test/src/functional_test_runner/functional_test_runner.ts b/packages/kbn-test/src/functional_test_runner/functional_test_runner.ts index b5d379d3426e7..4130cd8d138b8 100644 --- a/packages/kbn-test/src/functional_test_runner/functional_test_runner.ts +++ b/packages/kbn-test/src/functional_test_runner/functional_test_runner.ts @@ -78,18 +78,33 @@ export class FunctionalTestRunner { // replace the function of custom service providers so that they return // promise-like objects which never resolve, essentially disabling them // allowing us to load the test files and populate the mocha suites - const readStubbedProviderSpec = (type: string, providers: any) => + const readStubbedProviderSpec = (type: string, providers: any, skip: string[]) => readProviderSpec(type, providers).map((p) => ({ ...p, - fn: () => ({ - then: () => {}, - }), + fn: skip.includes(p.name) + ? (...args: unknown[]) => { + const result = p.fn(...args); + if ('then' in result) { + throw new Error( + `Provider [${p.name}] returns a promise so it can't loaded during test analysis` + ); + } + + return result; + } + : () => ({ + then: () => {}, + }), })); const providers = new ProviderCollection(this.log, [ ...coreProviders, - ...readStubbedProviderSpec('Service', config.get('services')), - ...readStubbedProviderSpec('PageObject', config.get('pageObjects')), + ...readStubbedProviderSpec( + 'Service', + config.get('services'), + config.get('servicesRequiredForTestAnalysis') + ), + ...readStubbedProviderSpec('PageObject', config.get('pageObjects'), []), ]); const mocha = await setupMocha(this.lifecycle, this.log, config, providers); diff --git a/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts b/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts index 7fae313c68bd3..a9ceaa643a60f 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts @@ -89,6 +89,7 @@ export const schema = Joi.object() }) .default(), + servicesRequiredForTestAnalysis: Joi.array().items(Joi.string()).default([]), services: Joi.object().pattern(ID_PATTERN, Joi.func().required()).default(), pageObjects: Joi.object().pattern(ID_PATTERN, Joi.func().required()).default(), diff --git a/src/plugins/kibana_overview/public/components/overview/overview.tsx b/src/plugins/kibana_overview/public/components/overview/overview.tsx index 6a0279bd12465..5108150f7ff8d 100644 --- a/src/plugins/kibana_overview/public/components/overview/overview.tsx +++ b/src/plugins/kibana_overview/public/components/overview/overview.tsx @@ -84,6 +84,9 @@ export const Overview: FC = ({ newsFetchResult, solutions, features }) => solution: i18n.translate('kibanaOverview.noDataConfig.solutionName', { defaultMessage: `Analytics`, }), + pageTitle: i18n.translate('kibanaOverview.noDataConfig.pageTitle', { + defaultMessage: `Welcome to Analytics!`, + }), logo: 'logoKibana', actions: { elasticAgent: { diff --git a/test/api_integration/apis/console/proxy_route.ts b/test/api_integration/apis/console/proxy_route.ts index d8a5f57a41a6e..a208ef405306f 100644 --- a/test/api_integration/apis/console/proxy_route.ts +++ b/test/api_integration/apis/console/proxy_route.ts @@ -12,7 +12,8 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - describe('POST /api/console/proxy', () => { + // Failing: See https://github.com/elastic/kibana/issues/117674 + describe.skip('POST /api/console/proxy', () => { describe('system indices behavior', () => { it('returns warning header when making requests to .kibana index', async () => { return await supertest diff --git a/test/functional/page_objects/common_page.ts b/test/functional/page_objects/common_page.ts index 69fbf7e49df3c..3955e457b5ffc 100644 --- a/test/functional/page_objects/common_page.ts +++ b/test/functional/page_objects/common_page.ts @@ -279,6 +279,9 @@ export class CommonPageObject extends FtrService { this.log.debug(msg); throw new Error(msg); } + if (appName === 'discover') { + await this.browser.setLocalStorageItem('data.autocompleteFtuePopover', 'true'); + } return currentUrl; }); diff --git a/x-pack/plugins/apm/server/lib/metrics/transform_metrics_chart.ts b/x-pack/plugins/apm/server/lib/metrics/transform_metrics_chart.ts index 7eaa90845d652..f4829f2d5faa0 100644 --- a/x-pack/plugins/apm/server/lib/metrics/transform_metrics_chart.ts +++ b/x-pack/plugins/apm/server/lib/metrics/transform_metrics_chart.ts @@ -26,26 +26,30 @@ export function transformDataToMetricsChart( title: chartBase.title, key: chartBase.key, yUnit: chartBase.yUnit, - series: Object.keys(chartBase.series).map((seriesKey, i) => { - const overallValue = aggregations?.[seriesKey]?.value; + series: + result.hits.total.value > 0 + ? Object.keys(chartBase.series).map((seriesKey, i) => { + const overallValue = aggregations?.[seriesKey]?.value; - return { - title: chartBase.series[seriesKey].title, - key: seriesKey, - type: chartBase.type, - color: - chartBase.series[seriesKey].color || getVizColorForIndex(i, theme), - overallValue, - data: - timeseriesData?.buckets.map((bucket) => { - const { value } = bucket[seriesKey]; - const y = value === null || isNaN(value) ? null : value; return { - x: bucket.key, - y, + title: chartBase.series[seriesKey].title, + key: seriesKey, + type: chartBase.type, + color: + chartBase.series[seriesKey].color || + getVizColorForIndex(i, theme), + overallValue, + data: + timeseriesData?.buckets.map((bucket) => { + const { value } = bucket[seriesKey]; + const y = value === null || isNaN(value) ? null : value; + return { + x: bucket.key, + y, + }; + }) || [], }; - }) || [], - }; - }), + }) + : [], }; } diff --git a/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap index d6d6219440dad..95bd6106b9ff2 100644 --- a/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap @@ -98,6 +98,9 @@ Object { }, }, "size": 1, + "sort": Object { + "_score": "desc", + }, }, "terminate_after": 1, } diff --git a/x-pack/plugins/apm/server/lib/services/get_service_agent.ts b/x-pack/plugins/apm/server/lib/services/get_service_agent.ts index 4c9ff9f124b10..dc3fee20fdf68 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_agent.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_agent.ts @@ -13,7 +13,6 @@ import { } from '../../../common/elasticsearch_fieldnames'; import { rangeQuery } from '../../../../observability/server'; import { Setup } from '../helpers/setup_request'; -import { getProcessorEventForTransactions } from '../helpers/transactions'; interface ServiceAgent { agent?: { @@ -29,13 +28,11 @@ interface ServiceAgent { export async function getServiceAgent({ serviceName, setup, - searchAggregatedTransactions, start, end, }: { serviceName: string; setup: Setup; - searchAggregatedTransactions: boolean; start: number; end: number; }) { @@ -46,7 +43,7 @@ export async function getServiceAgent({ apm: { events: [ ProcessorEvent.error, - getProcessorEventForTransactions(searchAggregatedTransactions), + ProcessorEvent.transaction, ProcessorEvent.metric, ], }, @@ -71,6 +68,9 @@ export async function getServiceAgent({ }, }, }, + sort: { + _score: 'desc' as const, + }, }, }; diff --git a/x-pack/plugins/apm/server/lib/services/queries.test.ts b/x-pack/plugins/apm/server/lib/services/queries.test.ts index 4ed6f856d735b..30d89214959da 100644 --- a/x-pack/plugins/apm/server/lib/services/queries.test.ts +++ b/x-pack/plugins/apm/server/lib/services/queries.test.ts @@ -28,7 +28,6 @@ describe('services queries', () => { getServiceAgent({ serviceName: 'foo', setup, - searchAggregatedTransactions: false, start: 0, end: 50000, }) diff --git a/x-pack/plugins/apm/server/routes/services.ts b/x-pack/plugins/apm/server/routes/services.ts index 257aec216eb06..3af829d59d3fd 100644 --- a/x-pack/plugins/apm/server/routes/services.ts +++ b/x-pack/plugins/apm/server/routes/services.ts @@ -191,18 +191,9 @@ const serviceAgentRoute = createApmServerRoute({ const { serviceName } = params.path; const { start, end } = params.query; - const searchAggregatedTransactions = await getSearchAggregatedTransactions({ - apmEventClient: setup.apmEventClient, - config: setup.config, - start, - end, - kuery: '', - }); - return getServiceAgent({ serviceName, setup, - searchAggregatedTransactions, start, end, }); diff --git a/x-pack/plugins/data_visualizer/kibana.json b/x-pack/plugins/data_visualizer/kibana.json index e63a6b4fa2100..81fc0a2fdfe02 100644 --- a/x-pack/plugins/data_visualizer/kibana.json +++ b/x-pack/plugins/data_visualizer/kibana.json @@ -25,7 +25,8 @@ "kibanaReact", "maps", "esUiShared", - "fieldFormats" + "fieldFormats", + "charts" ], "extraPublicDirs": [ "common" diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/document_count_content/document_count_chart/document_count_chart.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/document_count_content/document_count_chart/document_count_chart.tsx index 6459fc4006cea..13b68d3b192cc 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/document_count_content/document_count_chart/document_count_chart.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/document_count_content/document_count_chart/document_count_chart.tsx @@ -6,16 +6,13 @@ */ import React, { FC, useCallback, useMemo } from 'react'; - import { i18n } from '@kbn/i18n'; - import { Axis, BarSeries, BrushEndListener, Chart, ElementClickListener, - niceTimeFormatter, Position, ScaleType, Settings, @@ -23,7 +20,9 @@ import { XYBrushEvent, } from '@elastic/charts'; import moment from 'moment'; +import { IUiSettingsClient } from 'kibana/public'; import { useDataVisualizerKibana } from '../../../../kibana_context'; +import { MULTILAYER_TIME_AXIS_STYLE } from '../../../../../../../../../src/plugins/charts/common'; export interface DocumentCountChartPoint { time: number | string; @@ -40,6 +39,16 @@ interface Props { const SPEC_ID = 'document_count'; +function getTimezone(uiSettings: IUiSettingsClient) { + if (uiSettings.isDefault('dateFormat:tz')) { + const detectedTimezone = moment.tz.guess(); + if (detectedTimezone) return detectedTimezone; + else return moment().format('Z'); + } else { + return uiSettings.get('dateFormat:tz', 'Browser'); + } +} + export const DocumentCountChart: FC = ({ width, chartPoints, @@ -48,9 +57,12 @@ export const DocumentCountChart: FC = ({ interval, }) => { const { - services: { data }, + services: { data, uiSettings, fieldFormats }, } = useDataVisualizerKibana(); + const xAxisFormatter = fieldFormats.deserialize({ id: 'date' }); + const useLegacyTimeAxis = uiSettings.get('visualization:useLegacyTimeAxis', false); + const seriesName = i18n.translate( 'xpack.dataVisualizer.dataGrid.field.documentCountChart.seriesLabel', { @@ -63,8 +75,6 @@ export const DocumentCountChart: FC = ({ max: timeRangeLatest, }; - const dateFormatter = niceTimeFormatter([timeRangeEarliest, timeRangeLatest]); - const adjustedChartPoints = useMemo(() => { // Display empty chart when no data in range if (chartPoints.length < 1) return [{ time: timeRangeEarliest, value: 0 }]; @@ -110,6 +120,8 @@ export const DocumentCountChart: FC = ({ timefilterUpdateHandler(range); }; + const timeZone = getTimezone(uiSettings); + return (
= ({ id="bottom" position={Position.Bottom} showOverlappingTicks={true} - tickFormat={dateFormatter} + tickFormat={(value) => xAxisFormatter.convert(value)} + timeAxisLayerCount={useLegacyTimeAxis ? 0 : 2} + style={useLegacyTimeAxis ? {} : MULTILAYER_TIME_AXIS_STYLE} /> = ({ xAccessor="time" yAccessors={['value']} data={adjustedChartPoints} + timeZone={timeZone} />
diff --git a/x-pack/plugins/data_visualizer/public/plugin.ts b/x-pack/plugins/data_visualizer/public/plugin.ts index df1a5ea406d76..dd1d2acccf8cd 100644 --- a/x-pack/plugins/data_visualizer/public/plugin.ts +++ b/x-pack/plugins/data_visualizer/public/plugin.ts @@ -22,6 +22,7 @@ import { getFileDataVisualizerComponent, getIndexDataVisualizerComponent } from import { getMaxBytesFormatted } from './application/common/util/get_max_bytes'; import { registerHomeAddData, registerHomeFeatureCatalogue } from './register_home'; import { registerEmbeddables } from './application/index_data_visualizer/embeddables'; +import { FieldFormatsStart } from '../../../../src/plugins/field_formats/public'; export interface DataVisualizerSetupDependencies { home?: HomePublicPluginSetup; @@ -36,6 +37,7 @@ export interface DataVisualizerStartDependencies { share: SharePluginStart; lens?: LensPublicStart; indexPatternFieldEditor?: IndexPatternFieldEditorStart; + fieldFormats: FieldFormatsStart; } export type DataVisualizerPluginSetup = ReturnType; diff --git a/x-pack/plugins/fleet/common/types/rest_spec/package_policy.ts b/x-pack/plugins/fleet/common/types/rest_spec/package_policy.ts index ed5f8e07098d4..b050a7c798a0b 100644 --- a/x-pack/plugins/fleet/common/types/rest_spec/package_policy.ts +++ b/x-pack/plugins/fleet/common/types/rest_spec/package_policy.ts @@ -67,6 +67,12 @@ export type DeletePackagePoliciesResponse = Array<{ export interface UpgradePackagePolicyBaseResponse { name?: string; + + // Support generic errors + statusCode?: number; + body?: { + message: string; + }; } export interface UpgradePackagePolicyDryRunResponseItem extends UpgradePackagePolicyBaseResponse { diff --git a/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts b/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts index 58463bfa5569d..f61890f852798 100644 --- a/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts @@ -192,6 +192,8 @@ export const deletePackagePolicyHandler: RequestHandler< } }; +// TODO: Separate the upgrade and dry-run processes into separate endpoints, and address +// duplicate logic in error handling as part of https://github.com/elastic/kibana/issues/63123 export const upgradePackagePolicyHandler: RequestHandler< unknown, unknown, @@ -212,6 +214,16 @@ export const upgradePackagePolicyHandler: RequestHandler< ); body.push(result); } + + const firstFatalError = body.find((item) => item.statusCode && item.statusCode !== 200); + + if (firstFatalError) { + return response.customError({ + statusCode: firstFatalError.statusCode!, + body: { message: firstFatalError.body!.message }, + }); + } + return response.ok({ body, }); @@ -222,6 +234,15 @@ export const upgradePackagePolicyHandler: RequestHandler< request.body.packagePolicyIds, { user } ); + + const firstFatalError = body.find((item) => item.statusCode && item.statusCode !== 200); + + if (firstFatalError) { + return response.customError({ + statusCode: firstFatalError.statusCode!, + body: { message: firstFatalError.body!.message }, + }); + } return response.ok({ body, }); diff --git a/x-pack/plugins/fleet/server/services/managed_package_policies.ts b/x-pack/plugins/fleet/server/services/managed_package_policies.ts index 306725ae01953..e78bc096b8711 100644 --- a/x-pack/plugins/fleet/server/services/managed_package_policies.ts +++ b/x-pack/plugins/fleet/server/services/managed_package_policies.ts @@ -72,7 +72,10 @@ export const upgradeManagedPackagePolicies = async ( ); if (dryRunResults.hasErrors) { - const errors = dryRunResults.diff?.[1].errors; + const errors = dryRunResults.diff + ? dryRunResults.diff?.[1].errors + : dryRunResults.body?.message; + appContextService .getLogger() .error( diff --git a/x-pack/plugins/fleet/server/services/package_policy.ts b/x-pack/plugins/fleet/server/services/package_policy.ts index 985351c3e981b..c4ef15f4e7ed9 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.ts @@ -620,13 +620,6 @@ class PackagePolicyService { success: true, }); } catch (error) { - // We only want to specifically handle validation errors for the new package policy. If a more severe or - // general error is thrown elsewhere during the upgrade process, we want to surface that directly in - // order to preserve any status code mappings, etc that might be included w/ the particular error type - if (!(error instanceof PackagePolicyValidationError)) { - throw error; - } - result.push({ id, success: false, @@ -704,10 +697,6 @@ class PackagePolicyService { hasErrors, }; } catch (error) { - if (!(error instanceof PackagePolicyValidationError)) { - throw error; - } - return { hasErrors: true, ...ingestErrorToResponseOptions(error), diff --git a/x-pack/plugins/ml/common/constants/trained_models.ts b/x-pack/plugins/ml/common/constants/trained_models.ts new file mode 100644 index 0000000000000..019189ea13c05 --- /dev/null +++ b/x-pack/plugins/ml/common/constants/trained_models.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const DEPLOYMENT_STATE = { + STARTED: 'started', + STARTING: 'starting', + STOPPING: 'stopping', +} as const; + +export type DeploymentState = typeof DEPLOYMENT_STATE[keyof typeof DEPLOYMENT_STATE]; diff --git a/x-pack/plugins/ml/common/types/locator.ts b/x-pack/plugins/ml/common/types/locator.ts index 79db780b791fd..e13dbf7c5b271 100644 --- a/x-pack/plugins/ml/common/types/locator.ts +++ b/x-pack/plugins/ml/common/types/locator.ts @@ -188,6 +188,10 @@ export interface TrainedModelsQueryState { modelId?: string; } +export interface TrainedModelsNodesQueryState { + nodeId?: string; +} + export type DataFrameAnalyticsUrlState = MLPageState< | typeof ML_PAGES.DATA_FRAME_ANALYTICS_JOBS_MANAGE | typeof ML_PAGES.DATA_FRAME_ANALYTICS_MAP @@ -255,7 +259,8 @@ export type MlLocatorState = | CalendarEditUrlState | FilterEditUrlState | MlGenericUrlState - | TrainedModelsUrlState; + | TrainedModelsUrlState + | TrainedModelsNodesUrlState; export type MlLocatorParams = MlLocatorState & SerializableRecord; @@ -265,3 +270,8 @@ export type TrainedModelsUrlState = MLPageState< typeof ML_PAGES.TRAINED_MODELS_MANAGE, TrainedModelsQueryState | undefined >; + +export type TrainedModelsNodesUrlState = MLPageState< + typeof ML_PAGES.TRAINED_MODELS_NODES, + TrainedModelsNodesQueryState | undefined +>; diff --git a/x-pack/plugins/ml/common/types/trained_models.ts b/x-pack/plugins/ml/common/types/trained_models.ts index 5ad1d85d9feb9..89b8a50846cb3 100644 --- a/x-pack/plugins/ml/common/types/trained_models.ts +++ b/x-pack/plugins/ml/common/types/trained_models.ts @@ -5,9 +5,10 @@ * 2.0. */ -import { DataFrameAnalyticsConfig } from './data_frame_analytics'; -import { FeatureImportanceBaseline, TotalFeatureImportance } from './feature_importance'; -import { XOR } from './common'; +import type { DataFrameAnalyticsConfig } from './data_frame_analytics'; +import type { FeatureImportanceBaseline, TotalFeatureImportance } from './feature_importance'; +import type { XOR } from './common'; +import type { DeploymentState } from '../constants/trained_models'; export interface IngestStats { count: number; @@ -17,8 +18,8 @@ export interface IngestStats { } export interface TrainedModelStat { - model_id: string; - pipeline_count: number; + model_id?: string; + pipeline_count?: number; inference_stats?: { failure_count: number; inference_count: number; @@ -100,6 +101,9 @@ export interface TrainedModelConfigResponse { tags: string[]; version: string; inference_config?: Record; + /** + * Associated pipelines. Extends response from the ES endpoint. + */ pipelines?: Record | null; } @@ -125,7 +129,7 @@ export interface TrainedModelDeploymentStatsResponse { model_size_bytes: number; inference_threads: number; model_threads: number; - state: string; + state: DeploymentState; allocation_status: { target_allocation_count: number; state: string; allocation_count: number }; nodes: Array<{ node: Record< @@ -150,24 +154,35 @@ export interface TrainedModelDeploymentStatsResponse { }>; } +export interface AllocatedModel { + inference_threads: number; + allocation_status: { + target_allocation_count: number; + state: string; + allocation_count: number; + }; + model_id: string; + state: string; + model_threads: number; + model_size_bytes: number; + node: { + average_inference_time_ms: number; + inference_count: number; + routing_state: { + routing_state: string; + reason?: string; + }; + last_access?: number; + }; +} + export interface NodeDeploymentStatsResponse { id: string; name: string; transport_address: string; attributes: Record; roles: string[]; - allocated_models: Array<{ - inference_threads: number; - allocation_status: { - target_allocation_count: number; - state: string; - allocation_count: number; - }; - model_id: string; - state: string; - model_threads: number; - model_size_bytes: number; - }>; + allocated_models: AllocatedModel[]; memory_overview: { machine_memory: { /** Total machine memory in bytes */ diff --git a/x-pack/plugins/ml/public/application/components/navigation_menu/main_tabs.tsx b/x-pack/plugins/ml/public/application/components/navigation_menu/main_tabs.tsx index 78fc10e77b2da..614db1ba0df9d 100644 --- a/x-pack/plugins/ml/public/application/components/navigation_menu/main_tabs.tsx +++ b/x-pack/plugins/ml/public/application/components/navigation_menu/main_tabs.tsx @@ -7,7 +7,7 @@ import React, { FC, useState, useEffect } from 'react'; -import { EuiPageHeader, EuiBetaBadge } from '@elastic/eui'; +import { EuiPageHeader } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { TabId } from './navigation_menu'; import { useMlKibana, useMlLocator, useNavigateToPath } from '../../contexts/kibana'; @@ -57,20 +57,6 @@ function getTabs(disableLinks: boolean): Tab[] { defaultMessage: 'Model Management', }), disabled: disableLinks, - betaTag: ( - - ), }, { id: 'datavisualizer', @@ -201,7 +187,6 @@ export const MainTabs: FC = ({ tabId, disableLinks }) => { }, 'data-test-subj': testSubject + (id === selectedTabId ? ' selected' : ''), isSelected: id === selectedTabId, - append: tab.betaTag, }; })} /> diff --git a/x-pack/plugins/ml/public/application/contexts/kibana/use_field_formatter.ts b/x-pack/plugins/ml/public/application/contexts/kibana/use_field_formatter.ts index 508ce66f40f47..d089a43b3fb39 100644 --- a/x-pack/plugins/ml/public/application/contexts/kibana/use_field_formatter.ts +++ b/x-pack/plugins/ml/public/application/contexts/kibana/use_field_formatter.ts @@ -6,12 +6,26 @@ */ import { useMlKibana } from './kibana_context'; +import { FIELD_FORMAT_IDS } from '../../../../../../../src/plugins/field_formats/common'; -export function useFieldFormatter(fieldType: 'bytes') { +/** + * Set of reasonable defaults for formatters for the ML app. + */ +const defaultParam = { + [FIELD_FORMAT_IDS.DURATION]: { + inputFormat: 'milliseconds', + outputFormat: 'humanizePrecise', + }, +} as Record; + +export function useFieldFormatter(fieldType: FIELD_FORMAT_IDS) { const { services: { fieldFormats }, } = useMlKibana(); - const fieldFormatter = fieldFormats.deserialize({ id: fieldType }); + const fieldFormatter = fieldFormats.deserialize({ + id: fieldType, + params: defaultParam[fieldType], + }); return fieldFormatter.convert.bind(fieldFormatter); } diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/expanded_row.tsx b/x-pack/plugins/ml/public/application/trained_models/models_management/expanded_row.tsx index 4b342fe02b4d5..6dd7db1dbb7b6 100644 --- a/x-pack/plugins/ml/public/application/trained_models/models_management/expanded_row.tsx +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/expanded_row.tsx @@ -5,7 +5,8 @@ * 2.0. */ -import React, { FC, Fragment } from 'react'; +import React, { FC, Fragment, useEffect, useState } from 'react'; +import { omit } from 'lodash'; import { EuiBadge, EuiButtonEmpty, @@ -15,6 +16,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, + EuiListGroup, EuiNotificationBadge, EuiPanel, EuiSpacer, @@ -25,11 +27,13 @@ import { } from '@elastic/eui'; import { EuiDescriptionListProps } from '@elastic/eui/src/components/description_list/description_list'; import { FormattedMessage } from '@kbn/i18n/react'; +import type { EuiListGroupItemProps } from '@elastic/eui/src/components/list_group/list_group_item'; import { ModelItemFull } from './models_list'; -import { useMlKibana } from '../../contexts/kibana'; +import { useMlKibana, useMlLocator } from '../../contexts/kibana'; import { timeFormatter } from '../../../../common/util/date_utils'; import { isDefined } from '../../../../common/types/guards'; import { isPopulatedObject } from '../../../../common'; +import { ML_PAGES } from '../../../../common/constants/locator'; interface ExpandedRowProps { item: ModelItemFull; @@ -85,6 +89,12 @@ export function formatToListItems( } export const ExpandedRow: FC = ({ item }) => { + const mlLocator = useMlLocator(); + + const [deploymentStatsItems, setDeploymentStats] = useState( + [] + ); + const { inference_config: inferenceConfig, stats, @@ -119,6 +129,42 @@ export const ExpandedRow: FC = ({ item }) => { services: { share }, } = useMlKibana(); + useEffect( + function updateDeploymentState() { + (async function () { + const { nodes, ...deploymentStats } = stats.deployment_stats ?? {}; + + if (!isPopulatedObject(deploymentStats)) return; + + const result = formatToListItems(deploymentStats)!; + + const items: EuiListGroupItemProps[] = await Promise.all( + nodes!.map(async (v) => { + const nodeObject = Object.values(v.node)[0]; + const href = await mlLocator!.getUrl({ + page: ML_PAGES.TRAINED_MODELS_NODES, + pageState: { + nodeId: nodeObject.name, + }, + }); + return { + label: nodeObject.name, + href, + }; + }) + ); + + result.push({ + title: 'nodes', + description: , + }); + + setDeploymentStats(result); + })(); + }, + [stats.deployment_stats] + ); + const tabs = [ { id: 'details', @@ -234,164 +280,168 @@ export const ExpandedRow: FC = ({ item }) => { }, ] : []), - { - id: 'stats', - name: ( - - ), - content: ( - <> - - {stats.deployment_stats && ( - <> - - -
- -
-
+ ...(isPopulatedObject(omit(stats, 'pipeline_count')) + ? [ + { + id: 'stats', + name: ( + + ), + content: ( + <> - -
- - - )} - - {stats.inference_stats && ( - - - -
- -
-
- - -
-
- )} - {stats.ingest?.total && ( - - - -
- -
-
- - - - {stats.ingest?.pipelines && ( - <> - + {!!deploymentStatsItems?.length ? ( + <> +
- - {Object.entries(stats.ingest.pipelines).map( - ([pipelineName, { processors, ...pipelineStats }], i) => { - return ( - - - - - -
- {i + 1}. {pipelineName} -
-
-
-
- - - -
- - - - -
- -
-
- - <> - {processors.map((processor) => { - const name = Object.keys(processor)[0]; - const { stats: processorStats } = processor[name]; - return ( - - - - - -
{name}
-
-
-
- - - -
- - -
- ); - })} - -
- ); - } - )} - + + +
+ + + ) : null} + + {stats.inference_stats && ( + + + +
+ +
+
+ + +
+
)} -
-
- )} -
- - ), - }, + {stats.ingest?.total && ( + + + +
+ +
+
+ + + + {stats.ingest?.pipelines && ( + <> + + +
+ +
+
+ + {Object.entries(stats.ingest.pipelines).map( + ([pipelineName, { processors, ...pipelineStats }], i) => { + return ( + + + + + +
+ {i + 1}. {pipelineName} +
+
+
+
+ + + +
+ + + + +
+ +
+
+ + <> + {processors.map((processor) => { + const name = Object.keys(processor)[0]; + const { stats: processorStats } = processor[name]; + return ( + + + + + +
{name}
+
+
+
+ + + +
+ + +
+ ); + })} + +
+ ); + } + )} + + )} +
+
+ )} + + + ), + }, + ] + : []), ...(pipelines && Object.keys(pipelines).length > 0 ? [ { diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/models_list.tsx b/x-pack/plugins/ml/public/application/trained_models/models_management/models_list.tsx index 16b9aa760f535..9c3cc1f93a9cd 100644 --- a/x-pack/plugins/ml/public/application/trained_models/models_management/models_list.tsx +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/models_list.tsx @@ -5,19 +5,19 @@ * 2.0. */ -import React, { FC, useState, useCallback, useMemo } from 'react'; -import { groupBy } from 'lodash'; +import React, { FC, useCallback, useEffect, useMemo, useState } from 'react'; +import { omit } from 'lodash'; import { - EuiInMemoryTable, + EuiBadge, + EuiButton, + EuiButtonIcon, EuiFlexGroup, EuiFlexItem, - EuiTitle, - EuiButton, + EuiInMemoryTable, + EuiSearchBarProps, EuiSpacer, - EuiButtonIcon, - EuiBadge, + EuiTitle, SearchFilterConfig, - EuiSearchBarProps, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -48,9 +48,12 @@ import { ListingPageUrlState } from '../../../../common/types/common'; import { usePageUrlState } from '../../util/url_state'; import { ExpandedRow } from './expanded_row'; import { isPopulatedObject } from '../../../../common'; -import { timeFormatter } from '../../../../common/util/date_utils'; import { useTableSettings } from '../../data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings'; import { useToastNotificationService } from '../../services/toast_notification_service'; +import { useFieldFormatter } from '../../contexts/kibana/use_field_formatter'; +import { FIELD_FORMAT_IDS } from '../../../../../../../src/plugins/field_formats/common'; +import { useRefresh } from '../../routing/use_refresh'; +import { DEPLOYMENT_STATE } from '../../../../common/constants/trained_models'; type Stats = Omit; @@ -82,11 +85,15 @@ export const ModelsList: FC = () => { } = useMlKibana(); const urlLocator = useMlLocator()!; + const dateFormatter = useFieldFormatter(FIELD_FORMAT_IDS.DATE); + const [pageState, updatePageState] = usePageUrlState( ML_PAGES.TRAINED_MODELS_MANAGE, getDefaultModelsListState() ); + const refresh = useRefresh(); + const searchQueryText = pageState.queryText ?? ''; const canDeleteDataFrameAnalytics = capabilities.ml.canDeleteDataFrameAnalytics as boolean; @@ -121,7 +128,7 @@ export const ModelsList: FC = () => { size: 1000, }); - const newItems = []; + const newItems: ModelItem[] = []; const expandedItemsToRefresh = []; for (const model of response) { @@ -145,6 +152,11 @@ export const ModelsList: FC = () => { } } + // Need to fetch state for 3rd party models to enable/disable actions + await fetchAndPopulateDeploymentStats( + newItems.filter((v) => v.model_type.includes('pytorch')) + ); + setItems(newItems); if (expandedItemsToRefresh.length > 0) { @@ -175,6 +187,13 @@ export const ModelsList: FC = () => { onRefresh: fetchModelsData, }); + useEffect( + function updateOnTimerRefresh() { + fetchModelsData(); + }, + [refresh] + ); + const modelsStats: ModelsBarStats = useMemo(() => { return { total: { @@ -191,8 +210,6 @@ export const ModelsList: FC = () => { * Fetches models stats and update the original object */ const fetchModelsStats = useCallback(async (models: ModelItem[]) => { - const { true: pytorchModels } = groupBy(models, (m) => m.model_type === 'pytorch'); - try { if (models) { const { trained_model_stats: modelsStatsResponse } = @@ -200,19 +217,12 @@ export const ModelsList: FC = () => { for (const { model_id: id, ...stats } of modelsStatsResponse) { const model = models.find((m) => m.model_id === id); - model!.stats = stats; - } - } - - if (pytorchModels) { - const { deployment_stats: deploymentStatsResponse } = - await trainedModelsApiService.getTrainedModelDeploymentStats( - pytorchModels.map((m) => m.model_id) - ); - - for (const { model_id: id, ...stats } of deploymentStatsResponse) { - const model = models.find((m) => m.model_id === id); - model!.stats!.deployment_stats = stats; + if (model) { + model.stats = { + ...(model.stats ?? {}), + ...stats, + }; + } } } @@ -227,6 +237,39 @@ export const ModelsList: FC = () => { } }, []); + /** + * Updates model items with deployment stats; + * + * We have to fetch all deployment stats on each update, + * because for stopped models the API returns 404 response. + */ + const fetchAndPopulateDeploymentStats = useCallback(async (modelItems: ModelItem[]) => { + try { + const { deployment_stats: deploymentStats } = + await trainedModelsApiService.getTrainedModelDeploymentStats('*'); + + for (const deploymentStat of deploymentStats) { + const deployedModel = modelItems.find( + (model) => model.model_id === deploymentStat.model_id + ); + + if (deployedModel) { + deployedModel.stats = { + ...(deployedModel.stats ?? {}), + deployment_stats: omit(deploymentStat, 'model_id'), + }; + } + } + } catch (error) { + displayErrorToast( + error, + i18n.translate('xpack.ml.trainedModels.modelsList.fetchDeploymentStatsErrorMessage', { + defaultMessage: 'Fetch deployment stats failed', + }) + ); + } + }, []); + /** * Unique inference types from models */ @@ -361,12 +404,19 @@ export const ModelsList: FC = () => { description: i18n.translate('xpack.ml.inference.modelsList.startModelAllocationActionLabel', { defaultMessage: 'Start allocation', }), - icon: 'download', + icon: 'play', type: 'icon', isPrimary: true, + enabled: (item) => { + const { state } = item.stats?.deployment_stats ?? {}; + return ( + !isLoading && state !== DEPLOYMENT_STATE.STARTED && state !== DEPLOYMENT_STATE.STARTING + ); + }, available: (item) => item.model_type === 'pytorch', onClick: async (item) => { try { + setIsLoading(true); await trainedModelsApiService.startModelAllocation(item.model_id); displaySuccessToast( i18n.translate('xpack.ml.trainedModels.modelsList.startSuccess', { @@ -376,6 +426,7 @@ export const ModelsList: FC = () => { }, }) ); + await fetchModelsData(); } catch (e) { displayErrorToast( e, @@ -386,6 +437,7 @@ export const ModelsList: FC = () => { }, }) ); + setIsLoading(false); } }, }, @@ -400,9 +452,14 @@ export const ModelsList: FC = () => { type: 'icon', isPrimary: true, available: (item) => item.model_type === 'pytorch', - enabled: (item) => !isPopulatedObject(item.pipelines), + enabled: (item) => + !isLoading && + !isPopulatedObject(item.pipelines) && + isPopulatedObject(item.stats?.deployment_stats) && + item.stats?.deployment_stats?.state !== DEPLOYMENT_STATE.STOPPING, onClick: async (item) => { try { + setIsLoading(true); await trainedModelsApiService.stopModelAllocation(item.model_id); displaySuccessToast( i18n.translate('xpack.ml.trainedModels.modelsList.stopSuccess', { @@ -412,6 +469,8 @@ export const ModelsList: FC = () => { }, }) ); + // Need to fetch model state updates + await fetchModelsData(); } catch (e) { displayErrorToast( e, @@ -422,6 +481,7 @@ export const ModelsList: FC = () => { }, }) ); + setIsLoading(false); } }, }, @@ -521,13 +581,25 @@ export const ModelsList: FC = () => { ), 'data-test-subj': 'mlModelsTableColumnType', }, + { + name: i18n.translate('xpack.ml.trainedModels.modelsList.stateHeader', { + defaultMessage: 'State', + }), + sortable: (item) => item.stats?.deployment_stats?.state, + align: 'left', + render: (model: ModelItem) => { + const state = model.stats?.deployment_stats?.state; + return state ? {state} : null; + }, + 'data-test-subj': 'mlModelsTableColumnDeploymentState', + }, { field: ModelsTableToConfigMapping.createdAt, name: i18n.translate('xpack.ml.trainedModels.modelsList.createdAtHeader', { defaultMessage: 'Created at', }), dataType: 'date', - render: timeFormatter, + render: (v: number) => dateFormatter(v), sortable: true, 'data-test-subj': 'mlModelsTableColumnCreatedAt', }, diff --git a/x-pack/plugins/ml/public/application/trained_models/nodes_overview/allocated_models.tsx b/x-pack/plugins/ml/public/application/trained_models/nodes_overview/allocated_models.tsx new file mode 100644 index 0000000000000..2aad8183b7998 --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/nodes_overview/allocated_models.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, { FC } from 'react'; +import { EuiBadge, EuiInMemoryTable, EuiToolTip } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { EuiBasicTableColumn } from '@elastic/eui/src/components/basic_table/basic_table'; +import type { + AllocatedModel, + NodeDeploymentStatsResponse, +} from '../../../../common/types/trained_models'; +import { useFieldFormatter } from '../../contexts/kibana/use_field_formatter'; +import { FIELD_FORMAT_IDS } from '../../../../../../../src/plugins/field_formats/common'; + +interface AllocatedModelsProps { + models: NodeDeploymentStatsResponse['allocated_models']; +} + +export const AllocatedModels: FC = ({ models }) => { + const bytesFormatter = useFieldFormatter(FIELD_FORMAT_IDS.BYTES); + const dateFormatter = useFieldFormatter(FIELD_FORMAT_IDS.DATE); + const durationFormatter = useFieldFormatter(FIELD_FORMAT_IDS.DURATION); + + const columns: Array> = [ + { + field: 'model_id', + name: i18n.translate('xpack.ml.trainedModels.nodesList.modelsList.modelNameHeader', { + defaultMessage: 'Name', + }), + width: '300px', + sortable: true, + truncateText: false, + 'data-test-subj': 'mlAllocatedModelsTableName', + }, + { + name: i18n.translate('xpack.ml.trainedModels.nodesList.modelsList.modelSizeHeader', { + defaultMessage: 'Size', + }), + width: '100px', + truncateText: true, + 'data-test-subj': 'mlAllocatedModelsTableSize', + render: (v: AllocatedModel) => { + return bytesFormatter(v.model_size_bytes); + }, + }, + { + field: 'state', + name: i18n.translate('xpack.ml.trainedModels.nodesList.modelsList.modelStateHeader', { + defaultMessage: 'State', + }), + width: '100px', + truncateText: false, + 'data-test-subj': 'mlAllocatedModelsTableState', + }, + { + name: i18n.translate( + 'xpack.ml.trainedModels.nodesList.modelsList.modelAvgInferenceTimeHeader', + { + defaultMessage: 'Avg inference time', + } + ), + width: '100px', + truncateText: false, + 'data-test-subj': 'mlAllocatedModelsTableAvgInferenceTime', + render: (v: AllocatedModel) => { + return v.node.average_inference_time_ms + ? durationFormatter(v.node.average_inference_time_ms) + : '-'; + }, + }, + { + name: i18n.translate( + 'xpack.ml.trainedModels.nodesList.modelsList.modelInferenceCountHeader', + { + defaultMessage: 'Inference count', + } + ), + width: '100px', + 'data-test-subj': 'mlAllocatedModelsTableInferenceCount', + render: (v: AllocatedModel) => { + return v.node.inference_count; + }, + }, + { + name: i18n.translate('xpack.ml.trainedModels.nodesList.modelsList.modelLastAccessHeader', { + defaultMessage: 'Last access', + }), + width: '200px', + 'data-test-subj': 'mlAllocatedModelsTableInferenceCount', + render: (v: AllocatedModel) => { + return dateFormatter(v.node.last_access); + }, + }, + { + name: i18n.translate('xpack.ml.trainedModels.nodesList.modelsList.modelRoutingStateHeader', { + defaultMessage: 'Routing state', + }), + width: '100px', + 'data-test-subj': 'mlAllocatedModelsTableRoutingState', + render: (v: AllocatedModel) => { + const { routing_state: routingState, reason } = v.node.routing_state; + + return ( + + {routingState} + + ); + }, + }, + ]; + + return ( + + allowNeutralSort={false} + columns={columns} + hasActions={false} + isExpandable={false} + isSelectable={false} + items={models} + itemId={'model_id'} + rowProps={(item) => ({ + 'data-test-subj': `mlAllocatedModelTableRow row-${item.model_id}`, + })} + onTableChange={() => {}} + data-test-subj={'mlNodesTable'} + /> + ); +}; diff --git a/x-pack/plugins/ml/public/application/trained_models/nodes_overview/expanded_row.tsx b/x-pack/plugins/ml/public/application/trained_models/nodes_overview/expanded_row.tsx index a32747185dcc8..508a5689e1c9b 100644 --- a/x-pack/plugins/ml/public/application/trained_models/nodes_overview/expanded_row.tsx +++ b/x-pack/plugins/ml/public/application/trained_models/nodes_overview/expanded_row.tsx @@ -9,17 +9,15 @@ import React, { FC } from 'react'; import { EuiDescriptionList, EuiFlexGrid, - EuiFlexGroup, EuiFlexItem, - EuiHorizontalRule, EuiPanel, EuiSpacer, - EuiTextColor, EuiTitle, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { NodeItemWithStats } from './nodes_list'; import { formatToListItems } from '../models_management/expanded_row'; +import { AllocatedModels } from './allocated_models'; interface ExpandedRowProps { item: NodeItemWithStats; @@ -55,8 +53,6 @@ export const ExpandedRow: FC = ({ item }) => { listItems={formatToListItems(details)} /> - - @@ -76,10 +72,10 @@ export const ExpandedRow: FC = ({ item }) => { listItems={formatToListItems(attributes)} /> + - - - {allocatedModels.length > 0 ? ( + {allocatedModels.length > 0 ? ( +
@@ -91,34 +87,10 @@ export const ExpandedRow: FC = ({ item }) => { - {allocatedModels.map(({ model_id: modelId, ...rest }) => { - return ( - <> - - - - -
{modelId}
-
-
-
- - - -
- - - - - ); - })} + - ) : null} - + + ) : null} ); diff --git a/x-pack/plugins/ml/public/application/trained_models/nodes_overview/memory_preview_chart.tsx b/x-pack/plugins/ml/public/application/trained_models/nodes_overview/memory_preview_chart.tsx index ba790ba1c2576..dd9b6f8253860 100644 --- a/x-pack/plugins/ml/public/application/trained_models/nodes_overview/memory_preview_chart.tsx +++ b/x-pack/plugins/ml/public/application/trained_models/nodes_overview/memory_preview_chart.tsx @@ -8,25 +8,26 @@ import { i18n } from '@kbn/i18n'; import React, { FC, useMemo } from 'react'; import { - Chart, - Settings, - BarSeries, - ScaleType, Axis, + BarSeries, + Chart, Position, + ScaleType, SeriesColorAccessor, + Settings, } from '@elastic/charts'; import { euiPaletteGray } from '@elastic/eui'; import { NodeDeploymentStatsResponse } from '../../../../common/types/trained_models'; import { useFieldFormatter } from '../../contexts/kibana/use_field_formatter'; import { useCurrentEuiTheme } from '../../components/color_range_legend'; +import { FIELD_FORMAT_IDS } from '../../../../../../../src/plugins/field_formats/common'; interface MemoryPreviewChartProps { memoryOverview: NodeDeploymentStatsResponse['memory_overview']; } export const MemoryPreviewChart: FC = ({ memoryOverview }) => { - const bytesFormatter = useFieldFormatter('bytes'); + const bytesFormatter = useFieldFormatter(FIELD_FORMAT_IDS.BYTES); const { euiTheme } = useCurrentEuiTheme(); @@ -112,7 +113,7 @@ export const MemoryPreviewChart: FC = ({ memoryOverview tooltip={{ headerFormatter: ({ value }) => i18n.translate('xpack.ml.trainedModels.nodesList.memoryBreakdown', { - defaultMessage: 'Approximate memory breakdown based on the node info', + defaultMessage: 'Approximate memory breakdown', }), }} /> diff --git a/x-pack/plugins/ml/public/application/trained_models/nodes_overview/nodes_list.tsx b/x-pack/plugins/ml/public/application/trained_models/nodes_overview/nodes_list.tsx index 42e51f1ab2971..b1cc18e698c9d 100644 --- a/x-pack/plugins/ml/public/application/trained_models/nodes_overview/nodes_list.tsx +++ b/x-pack/plugins/ml/public/application/trained_models/nodes_overview/nodes_list.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { FC, useCallback, useMemo, useState } from 'react'; +import React, { FC, useCallback, useEffect, useMemo, useState } from 'react'; import { EuiButtonIcon, EuiFlexGroup, @@ -31,6 +31,8 @@ import { MemoryPreviewChart } from './memory_preview_chart'; import { useFieldFormatter } from '../../contexts/kibana/use_field_formatter'; import { ListingPageUrlState } from '../../../../common/types/common'; import { useToastNotificationService } from '../../services/toast_notification_service'; +import { FIELD_FORMAT_IDS } from '../../../../../../../src/plugins/field_formats/common'; +import { useRefresh } from '../../routing/use_refresh'; export type NodeItem = NodeDeploymentStatsResponse; @@ -47,8 +49,11 @@ export const getDefaultNodesListState = (): ListingPageUrlState => ({ export const NodesList: FC = () => { const trainedModelsApiService = useTrainedModelsApiService(); + + const refresh = useRefresh(); + const { displayErrorToast } = useToastNotificationService(); - const bytesFormatter = useFieldFormatter('bytes'); + const bytesFormatter = useFieldFormatter(FIELD_FORMAT_IDS.BYTES); const [items, setItems] = useState([]); const [isLoading, setIsLoading] = useState(false); const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState>( @@ -179,6 +184,13 @@ export const NodesList: FC = () => { onRefresh: fetchNodesData, }); + useEffect( + function updateOnTimerRefresh() { + fetchNodesData(); + }, + [refresh] + ); + return ( <> diff --git a/x-pack/plugins/ml/public/application/trained_models/page.tsx b/x-pack/plugins/ml/public/application/trained_models/page.tsx index a6d99ca0fedc0..54849f3e651df 100644 --- a/x-pack/plugins/ml/public/application/trained_models/page.tsx +++ b/x-pack/plugins/ml/public/application/trained_models/page.tsx @@ -10,6 +10,7 @@ import React, { FC, Fragment, useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { + EuiBetaBadge, EuiFlexGroup, EuiFlexItem, EuiPage, @@ -21,6 +22,7 @@ import { } from '@elastic/eui'; import { useLocation } from 'react-router-dom'; +import { i18n } from '@kbn/i18n'; import { NavigationMenu } from '../components/navigation_menu'; import { ModelsList } from './models_management'; import { TrainedModelsNavigationBar } from './navigation_bar'; @@ -44,14 +46,35 @@ export const Page: FC = () => { - -

- + + +

+ +

+
+
+ + -

-
+ +
diff --git a/x-pack/plugins/ml/public/locator/formatters/trained_models.ts b/x-pack/plugins/ml/public/locator/formatters/trained_models.ts index d084c0675769f..9e1c5d92ce451 100644 --- a/x-pack/plugins/ml/public/locator/formatters/trained_models.ts +++ b/x-pack/plugins/ml/public/locator/formatters/trained_models.ts @@ -5,8 +5,13 @@ * 2.0. */ -import { TrainedModelsUrlState } from '../../../common/types/locator'; +import type { + TrainedModelsNodesUrlState, + TrainedModelsUrlState, +} from '../../../common/types/locator'; import { ML_PAGES } from '../../../common/constants/locator'; +import type { AppPageState, ListingPageUrlState } from '../../../common/types/common'; +import { setStateToKbnUrl } from '../../../../../../src/plugins/kibana_utils/public'; export function formatTrainedModelsManagementUrl( appBasePath: string, @@ -14,3 +19,31 @@ export function formatTrainedModelsManagementUrl( ): string { return `${appBasePath}/${ML_PAGES.TRAINED_MODELS_MANAGE}`; } + +export function formatTrainedModelsNodesManagementUrl( + appBasePath: string, + mlUrlGeneratorState: TrainedModelsNodesUrlState['pageState'] +): string { + let url = `${appBasePath}/${ML_PAGES.TRAINED_MODELS_NODES}`; + if (mlUrlGeneratorState) { + const { nodeId } = mlUrlGeneratorState; + if (nodeId) { + const nodesListState: Partial = { + queryText: `name:(${nodeId})`, + }; + + const queryState: AppPageState = { + [ML_PAGES.TRAINED_MODELS_NODES]: nodesListState, + }; + + url = setStateToKbnUrl>( + '_a', + queryState, + { useHash: false, storeInHashQuery: false }, + url + ); + } + } + + return url; +} diff --git a/x-pack/plugins/ml/public/locator/ml_locator.ts b/x-pack/plugins/ml/public/locator/ml_locator.ts index 7fa573c3e653d..c79c93078d04a 100644 --- a/x-pack/plugins/ml/public/locator/ml_locator.ts +++ b/x-pack/plugins/ml/public/locator/ml_locator.ts @@ -26,7 +26,10 @@ import { formatEditCalendarUrl, formatEditFilterUrl, } from './formatters'; -import { formatTrainedModelsManagementUrl } from './formatters/trained_models'; +import { + formatTrainedModelsManagementUrl, + formatTrainedModelsNodesManagementUrl, +} from './formatters/trained_models'; export type { MlLocatorParams, MlLocator }; @@ -70,6 +73,9 @@ export class MlLocatorDefinition implements LocatorDefinition { case ML_PAGES.TRAINED_MODELS_MANAGE: path = formatTrainedModelsManagementUrl('', params.pageState); break; + case ML_PAGES.TRAINED_MODELS_NODES: + path = formatTrainedModelsNodesManagementUrl('', params.pageState); + break; case ML_PAGES.ANOMALY_DETECTION_CREATE_JOB: case ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_ADVANCED: case ML_PAGES.DATA_VISUALIZER: diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_upgrade_secuirty_packages.test.tsx b/x-pack/plugins/security_solution/public/common/hooks/use_upgrade_secuirty_packages.test.tsx index 968bd8679b23b..3e6dd399a3094 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_upgrade_secuirty_packages.test.tsx +++ b/x-pack/plugins/security_solution/public/common/hooks/use_upgrade_secuirty_packages.test.tsx @@ -9,6 +9,7 @@ import React, { memo } from 'react'; import { useKibana } from '../lib/kibana'; import { renderHook as _renderHook, RenderHookResult } from '@testing-library/react-hooks'; import { useUpgradeSecurityPackages } from './use_upgrade_security_packages'; +import { epmRouteService } from '../../../../fleet/common'; jest.mock('../components/user_privileges', () => { return { @@ -57,7 +58,7 @@ describe.skip('When using the `useUpgradeSecurityPackages()` hook', () => { ); expect(kibana.services.http.post).toHaveBeenCalledWith( - '/api/fleet/epm/packages/_bulk', + `${epmRouteService.getBulkInstallPath()}`, expect.objectContaining({ body: '{"packages":["endpoint","security_detection_engine"]}', }) diff --git a/x-pack/plugins/security_solution/public/common/utils/validators/index.test.ts b/x-pack/plugins/security_solution/public/common/utils/validators/index.test.ts index 2758a9724e897..2fbe0ed3e6bb8 100644 --- a/x-pack/plugins/security_solution/public/common/utils/validators/index.test.ts +++ b/x-pack/plugins/security_solution/public/common/utils/validators/index.test.ts @@ -14,19 +14,19 @@ describe('helpers', () => { }); test('should verify as invalid url without http(s):// prefix', () => { - expect(isUrlInvalid('www.thisIsNotValid.com')).toBeTruthy(); + expect(isUrlInvalid('www.thisIsNotValid.com/foo')).toBeTruthy(); }); test('verifies valid url', () => { - expect(isUrlInvalid('https://www.elastic.co/')).toBeFalsy(); + expect(isUrlInvalid('https://www.elastic.co/foo')).toBeFalsy(); }); test('should verify valid wwww such as 4 of them.', () => { - expect(isUrlInvalid('https://wwww.example.com')).toBeFalsy(); + expect(isUrlInvalid('https://wwww.example.com/foo')).toBeFalsy(); }); test('should validate characters such as %22 being part of a correct URL.', () => { - expect(isUrlInvalid('https://www.exam%22ple.com')).toBeFalsy(); + expect(isUrlInvalid('https://www.exam%22ple.com/foo')).toBeFalsy(); }); test('should validate characters incorrectly such as ]', () => { @@ -34,7 +34,7 @@ describe('helpers', () => { }); test('should verify valid http url', () => { - expect(isUrlInvalid('http://www.example.com/')).toBeFalsy(); + expect(isUrlInvalid('http://www.example.com/foo')).toBeFalsy(); }); test('should verify as valid when given an empty string', () => { @@ -44,5 +44,9 @@ describe('helpers', () => { test('empty spaces should valid as not valid ', () => { expect(isUrlInvalid(' ')).toBeTruthy(); }); + + test('should verify as invalid url without //', () => { + expect(isUrlInvalid('http:www.thisIsNotValid.com/foo')).toBeTruthy(); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/common/utils/validators/index.ts b/x-pack/plugins/security_solution/public/common/utils/validators/index.ts index 7f0e8c30177f8..6989e27fc779d 100644 --- a/x-pack/plugins/security_solution/public/common/utils/validators/index.ts +++ b/x-pack/plugins/security_solution/public/common/utils/validators/index.ts @@ -16,7 +16,10 @@ export const isUrlInvalid = (url: string | null | undefined) => { return false; } else { const urlParsed = new URL(url); - if (allowedSchemes.includes(urlParsed.protocol)) { + if ( + allowedSchemes.includes(urlParsed.protocol) && + url.startsWith(`${urlParsed.protocol}//`) + ) { return false; } } diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/translations.ts b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/translations.ts index 48516aa203b68..f1841430f03ff 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/translations.ts @@ -73,7 +73,7 @@ export const CRITICAL = i18n.translate( export const URL_FORMAT_INVALID = i18n.translate( 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.referencesUrlInvalidError', { - defaultMessage: 'Url is invalid format', + defaultMessage: 'URL is invalid format', } ); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/services/ingest.test.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/services/ingest.test.ts index 9de5e2806e857..b5897d8fd3bc4 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/services/ingest.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/services/ingest.test.ts @@ -12,7 +12,12 @@ import { sendGetEndpointSpecificPackagePolicies, } from './ingest'; import { httpServiceMock } from '../../../../../../../../../src/core/public/mocks'; -import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../../../../../../../fleet/common'; +import { + EPM_API_ROUTES, + PACKAGE_POLICY_SAVED_OBJECT_TYPE, + PACKAGE_POLICY_API_ROOT, + PACKAGE_POLICY_API_ROUTES, +} from '../../../../../../../fleet/common'; import { policyListApiPathHandlers } from '../test_mock_utils'; describe('ingest service', () => { @@ -25,7 +30,7 @@ describe('ingest service', () => { describe('sendGetEndpointSpecificPackagePolicies()', () => { it('auto adds kuery to api request', async () => { await sendGetEndpointSpecificPackagePolicies(http); - expect(http.get).toHaveBeenCalledWith('/api/fleet/package_policies', { + expect(http.get).toHaveBeenCalledWith(`${PACKAGE_POLICY_API_ROUTES.LIST_PATTERN}`, { query: { kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name: endpoint`, }, @@ -35,7 +40,7 @@ describe('ingest service', () => { await sendGetEndpointSpecificPackagePolicies(http, { query: { kuery: 'someValueHere', page: 1, perPage: 10 }, }); - expect(http.get).toHaveBeenCalledWith('/api/fleet/package_policies', { + expect(http.get).toHaveBeenCalledWith(`${PACKAGE_POLICY_API_ROUTES.LIST_PATTERN}`, { query: { kuery: `someValueHere and ${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name: endpoint`, perPage: 10, @@ -48,11 +53,11 @@ describe('ingest service', () => { describe('sendGetPackagePolicy()', () => { it('builds correct API path', async () => { await sendGetPackagePolicy(http, '123'); - expect(http.get).toHaveBeenCalledWith('/api/fleet/package_policies/123', undefined); + expect(http.get).toHaveBeenCalledWith(`${PACKAGE_POLICY_API_ROOT}/123`, undefined); }); it('supports http options', async () => { await sendGetPackagePolicy(http, '123', { query: { page: 1 } }); - expect(http.get).toHaveBeenCalledWith('/api/fleet/package_policies/123', { + expect(http.get).toHaveBeenCalledWith(`${PACKAGE_POLICY_API_ROOT}/123`, { query: { page: 1, }, @@ -66,7 +71,7 @@ describe('ingest service', () => { Promise.resolve(policyListApiPathHandlers()[INGEST_API_EPM_PACKAGES]()) ); await sendGetEndpointSecurityPackage(http); - expect(http.get).toHaveBeenCalledWith('/api/fleet/epm/packages', { + expect(http.get).toHaveBeenCalledWith(`${EPM_API_ROUTES.LIST_PATTERN}`, { query: { category: 'security' }, }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx index c176ce9cacd43..2e763f3f4eaf3 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx @@ -13,6 +13,7 @@ import { EndpointDocGenerator } from '../../../../../common/endpoint/generate_da import { AppContextTestRender, createAppRootMockRenderer } from '../../../../common/mock/endpoint'; import { getPolicyDetailPath, getEndpointListPath } from '../../../common/routing'; import { policyListApiPathHandlers } from '../store/test_mock_utils'; +import { PACKAGE_POLICY_API_ROOT, AGENT_API_ROUTES } from '../../../../../../fleet/common'; jest.mock('./policy_forms/components/policy_form_layout'); @@ -80,7 +81,7 @@ describe('Policy Details', () => { const [path] = args; if (typeof path === 'string') { // GET datasouce - if (path === '/api/fleet/package_policies/1') { + if (path === `${PACKAGE_POLICY_API_ROOT}/1`) { asyncActions = asyncActions.then(async (): Promise => sleep()); return Promise.resolve({ item: policyPackagePolicy, @@ -89,7 +90,7 @@ describe('Policy Details', () => { } // GET Agent status for agent policy - if (path === '/api/fleet/agent-status') { + if (path === `${AGENT_API_ROUTES.STATUS_PATTERN}`) { asyncActions = asyncActions.then(async () => sleep()); return Promise.resolve({ results: { events: 0, total: 5, online: 3, error: 1, offline: 1 }, diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/components/policy_form_layout.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/components/policy_form_layout.test.tsx index 28f2ecf7eb98e..2702aec1cbabe 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/components/policy_form_layout.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/components/policy_form_layout.test.tsx @@ -18,6 +18,7 @@ import { import { getPolicyDetailPath, getEndpointListPath } from '../../../../../common/routing'; import { policyListApiPathHandlers } from '../../../store/test_mock_utils'; import { licenseService } from '../../../../../../common/hooks/use_license'; +import { PACKAGE_POLICY_API_ROOT, AGENT_API_ROUTES } from '../../../../../../../../fleet/common'; jest.mock('../../../../../../common/hooks/use_license'); @@ -64,7 +65,7 @@ describe('Policy Form Layout', () => { const [path] = args; if (typeof path === 'string') { // GET datasouce - if (path === '/api/fleet/package_policies/1') { + if (path === `${PACKAGE_POLICY_API_ROOT}/1`) { asyncActions = asyncActions.then(async (): Promise => sleep()); return Promise.resolve({ item: policyPackagePolicy, @@ -73,7 +74,7 @@ describe('Policy Form Layout', () => { } // GET Agent status for agent policy - if (path === '/api/fleet/agent-status') { + if (path === `${AGENT_API_ROUTES.STATUS_PATTERN}`) { asyncActions = asyncActions.then(async () => sleep()); return Promise.resolve({ results: { events: 0, total: 5, online: 3, error: 1, offline: 1 }, @@ -156,7 +157,7 @@ describe('Policy Form Layout', () => { asyncActions = asyncActions.then(async () => sleep()); const [path] = args; if (typeof path === 'string') { - if (path === '/api/fleet/package_policies/1') { + if (path === `${PACKAGE_POLICY_API_ROOT}/1`) { return Promise.resolve({ item: policyPackagePolicy, success: true, @@ -201,7 +202,7 @@ describe('Policy Form Layout', () => { // API should be called await asyncActions; - expect(http.put.mock.calls[0][0]).toEqual(`/api/fleet/package_policies/1`); + expect(http.put.mock.calls[0][0]).toEqual(`${PACKAGE_POLICY_API_ROOT}/1`); policyFormLayoutView.update(); // Toast notification should be shown diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.test.tsx index dbb18a1b0f2ef..318b98712a7c0 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.test.tsx @@ -21,6 +21,7 @@ import { EndpointDocGenerator } from '../../../../../../../common/endpoint/gener import { policyListApiPathHandlers } from '../../../store/test_mock_utils'; import { useEndpointPrivileges } from '../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges'; import { getEndpointPrivilegesInitialStateMock } from '../../../../../../common/components/user_privileges/endpoint/mocks'; +import { PACKAGE_POLICY_API_ROOT, AGENT_API_ROUTES } from '../../../../../../../../fleet/common'; jest.mock('../../../../trusted_apps/service'); jest.mock('../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges'); @@ -43,7 +44,7 @@ describe('Policy trusted apps layout', () => { const [path] = args; if (typeof path === 'string') { // GET datasouce - if (path === '/api/fleet/package_policies/1234') { + if (path === `${PACKAGE_POLICY_API_ROOT}/1234`) { return Promise.resolve({ item: generator.generatePolicyPackagePolicy(), success: true, @@ -51,7 +52,7 @@ describe('Policy trusted apps layout', () => { } // GET Agent status for agent policy - if (path === '/api/fleet/agent-status') { + if (path === `${AGENT_API_ROUTES.STATUS_PATTERN}`) { return Promise.resolve({ results: { events: 0, total: 5, online: 3, error: 1, offline: 1 }, success: true, diff --git a/x-pack/test/apm_api_integration/common/config.ts b/x-pack/test/apm_api_integration/common/config.ts index ab22522acac52..cee1fdbbc50b3 100644 --- a/x-pack/test/apm_api_integration/common/config.ts +++ b/x-pack/test/apm_api_integration/common/config.ts @@ -14,10 +14,10 @@ import { PromiseReturnType } from '../../../plugins/observability/typings/common import { createApmUser, APM_TEST_PASSWORD, ApmUser } from './authentication'; import { APMFtrConfigName } from '../configs'; import { createApmApiClient } from './apm_api_supertest'; -import { registry } from './registry'; +import { RegistryProvider } from './registry'; import { synthtraceEsClientService } from './synthtrace_es_client_service'; -interface Config { +export interface ApmFtrConfig { name: APMFtrConfigName; license: 'basic' | 'trial'; kibanaConfig?: Record; @@ -58,7 +58,7 @@ async function getApmApiClient( export type CreateTestConfig = ReturnType; -export function createTestConfig(config: Config) { +export function createTestConfig(config: ApmFtrConfig) { const { license, name, kibanaConfig } = config; return async ({ readConfigFile }: FtrConfigProviderContext) => { @@ -70,13 +70,14 @@ export function createTestConfig(config: Config) { const servers = xPackAPITestsConfig.get('servers'); const kibanaServer = servers.kibana; - registry.init(config.name); - return { testFiles: [require.resolve('../tests')], servers, + servicesRequiredForTestAnalysis: ['apmFtrConfig', 'registry'], services: { ...services, + apmFtrConfig: () => config, + registry: RegistryProvider, synthtraceEsClient: synthtraceEsClientService, apmApiClient: async (context: InheritedFtrProviderContext) => { const security = context.getService('security'); diff --git a/x-pack/test/apm_api_integration/common/registry.ts b/x-pack/test/apm_api_integration/common/registry.ts index 55b5863e6d444..ac59059e49d83 100644 --- a/x-pack/test/apm_api_integration/common/registry.ts +++ b/x-pack/test/apm_api_integration/common/registry.ts @@ -28,168 +28,165 @@ interface RunCondition { archives: ArchiveName[]; } -const callbacks: Array< - RunCondition & { - runs: Array<{ - cb: () => void; - }>; - } -> = []; +export function RegistryProvider({ getService }: FtrProviderContext) { + const apmFtrConfig = getService('apmFtrConfig'); + + const callbacks: Array< + RunCondition & { + runs: Array<{ + cb: () => void; + }>; + } + > = []; -let configName: APMFtrConfigName | undefined; + let running: boolean = false; -let running: boolean = false; + function when( + title: string, + conditions: RunCondition | RunCondition[], + callback: (condition: RunCondition) => void, + skip?: boolean + ) { + const allConditions = castArray(conditions); -function when( - title: string, - conditions: RunCondition | RunCondition[], - callback: (condition: RunCondition) => void, - skip?: boolean -) { - const allConditions = castArray(conditions); + if (!allConditions.length) { + throw new Error('At least one condition should be defined'); + } - if (!allConditions.length) { - throw new Error('At least one condition should be defined'); - } + if (running) { + throw new Error("Can't add tests when running"); + } - if (running) { - throw new Error("Can't add tests when running"); - } + const frame = maybe(callsites()[1]); - const frame = maybe(callsites()[1]); + const file = frame?.getFileName(); - const file = frame?.getFileName(); + if (!file) { + throw new Error('Could not infer file for suite'); + } - if (!file) { - throw new Error('Could not infer file for suite'); + allConditions.forEach((matchedCondition) => { + callbacks.push({ + ...matchedCondition, + runs: [ + { + cb: () => { + const suite: ReturnType = (skip ? describe.skip : describe)( + title, + () => { + callback(matchedCondition); + } + ) as any; + + suite.file = file; + suite.eachTest((test) => { + test.file = file; + }); + }, + }, + ], + }); + }); } - allConditions.forEach((matchedCondition) => { - callbacks.push({ - ...matchedCondition, - runs: [ - { - cb: () => { - const suite: ReturnType = (skip ? describe.skip : describe)( - title, - () => { - callback(matchedCondition); - } - ) as any; + when.skip = ( + title: string, + conditions: RunCondition | RunCondition[], + callback: (condition: RunCondition) => void + ) => { + when(title, conditions, callback, true); + }; - suite.file = file; - suite.eachTest((test) => { - test.file = file; - }); - }, - }, - ], - }); - }); -} + const registry = { + when, + run: () => { + running = true; -when.skip = ( - title: string, - conditions: RunCondition | RunCondition[], - callback: (condition: RunCondition) => void -) => { - when(title, conditions, callback, true); -}; - -export const registry = { - init: (config: APMFtrConfigName) => { - configName = config; - callbacks.length = 0; - running = false; - }, - when, - run: (context: FtrProviderContext) => { - if (!configName) { - throw new Error(`registry was not init() before running`); - } - running = true; - const esArchiver = context.getService('esArchiver'); - const logger = context.getService('log'); + const esArchiver = getService('esArchiver'); + const logger = getService('log'); - const supertest = context.getService('legacySupertestAsApmWriteUser'); + const supertest = getService('legacySupertestAsApmWriteUser'); - const logWithTimer = () => { - const start = process.hrtime(); + const logWithTimer = () => { + const start = process.hrtime(); - return (message: string) => { - const diff = process.hrtime(start); - const time = `${Math.round(diff[0] * 1000 + diff[1] / 1e6)}ms`; - logger.info(`(${time}) ${message}`); + return (message: string) => { + const diff = process.hrtime(start); + const time = `${Math.round(diff[0] * 1000 + diff[1] / 1e6)}ms`; + logger.info(`(${time}) ${message}`); + }; }; - }; - - const groups = joinByKey(callbacks, ['config', 'archives'], (a, b) => ({ - ...a, - ...b, - runs: a.runs.concat(b.runs), - })); - - callbacks.length = 0; - - const byConfig = groupBy(groups, 'config'); - - Object.keys(byConfig).forEach((config) => { - const groupsForConfig = byConfig[config]; - // register suites for other configs, but skip them so tests are marked as such - // and their snapshots are not marked as obsolete - (config === configName ? describe : describe.skip)(config, () => { - groupsForConfig.forEach((group) => { - const { runs, ...condition } = group; - - const runBefore = async () => { - const log = logWithTimer(); - for (const archiveName of condition.archives) { - log(`Loading ${archiveName}`); - - await esArchiver.load( - Path.join( - 'x-pack/test/apm_api_integration/common/fixtures/es_archiver', - archiveName - ) - ); - - // sync jobs from .ml-config to .kibana SOs - await supertest.get('/api/ml/saved_objects/sync').set('kbn-xsrf', 'foo'); - } - if (condition.archives.length) { - log('Loaded all archives'); - } - }; - - const runAfter = async () => { - const log = logWithTimer(); - for (const archiveName of condition.archives) { - log(`Unloading ${archiveName}`); - await esArchiver.unload( - Path.join( - 'x-pack/test/apm_api_integration/common/fixtures/es_archiver', - archiveName - ) - ); - } - if (condition.archives.length) { - log('Unloaded all archives'); - } - }; - - describe(condition.archives.join(',') || 'no data', () => { - before(runBefore); - - runs.forEach((run) => { - run.cb(); - }); - after(runAfter); + const groups = joinByKey(callbacks, ['config', 'archives'], (a, b) => ({ + ...a, + ...b, + runs: a.runs.concat(b.runs), + })); + + callbacks.length = 0; + + const byConfig = groupBy(groups, 'config'); + + Object.keys(byConfig).forEach((config) => { + const groupsForConfig = byConfig[config]; + // register suites for other configs, but skip them so tests are marked as such + // and their snapshots are not marked as obsolete + (config === apmFtrConfig.name ? describe : describe.skip)(config, () => { + groupsForConfig.forEach((group) => { + const { runs, ...condition } = group; + + const runBefore = async () => { + const log = logWithTimer(); + for (const archiveName of condition.archives) { + log(`Loading ${archiveName}`); + + await esArchiver.load( + Path.join( + 'x-pack/test/apm_api_integration/common/fixtures/es_archiver', + archiveName + ) + ); + + // sync jobs from .ml-config to .kibana SOs + await supertest.get('/api/ml/saved_objects/sync').set('kbn-xsrf', 'foo'); + } + if (condition.archives.length) { + log('Loaded all archives'); + } + }; + + const runAfter = async () => { + const log = logWithTimer(); + for (const archiveName of condition.archives) { + log(`Unloading ${archiveName}`); + await esArchiver.unload( + Path.join( + 'x-pack/test/apm_api_integration/common/fixtures/es_archiver', + archiveName + ) + ); + } + if (condition.archives.length) { + log('Unloaded all archives'); + } + }; + + describe(condition.archives.join(',') || 'no data', () => { + before(runBefore); + + runs.forEach((run) => { + run.cb(); + }); + + after(runAfter); + }); }); }); }); - }); - running = false; - }, -}; + running = false; + }, + }; + + return registry; +} diff --git a/x-pack/test/apm_api_integration/tests/alerts/chart_preview.ts b/x-pack/test/apm_api_integration/tests/alerts/chart_preview.ts index 621ed5dcfd8d7..63fe4fc67641a 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/chart_preview.ts +++ b/x-pack/test/apm_api_integration/tests/alerts/chart_preview.ts @@ -8,9 +8,9 @@ import expect from '@kbn/expect'; import archives from '../../common/fixtures/es_archiver/archives_metadata'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); const archiveName = 'apm_8.0.0'; const { end } = archives[archiveName]; diff --git a/x-pack/test/apm_api_integration/tests/alerts/rule_registry.ts b/x-pack/test/apm_api_integration/tests/alerts/rule_registry.ts index 06abeb02404c8..efa8aa3ace9dc 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/rule_registry.ts +++ b/x-pack/test/apm_api_integration/tests/alerts/rule_registry.ts @@ -18,7 +18,6 @@ import { } from '@kbn/rule-data-utils'; import { merge, omit } from 'lodash'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; interface Alert { schedule: { @@ -36,6 +35,7 @@ interface Alert { } export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const supertest = getService('legacySupertestAsApmWriteUser'); const es = getService('es'); diff --git a/x-pack/test/apm_api_integration/tests/correlations/failed_transactions.ts b/x-pack/test/apm_api_integration/tests/correlations/failed_transactions.ts index 3388d5b4aa379..a20852ef0ae54 100644 --- a/x-pack/test/apm_api_integration/tests/correlations/failed_transactions.ts +++ b/x-pack/test/apm_api_integration/tests/correlations/failed_transactions.ts @@ -14,10 +14,10 @@ import type { RawSearchStrategyClientParams } from '../../../../plugins/apm/comm import { APM_SEARCH_STRATEGIES } from '../../../../plugins/apm/common/search_strategies/constants'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; import { parseBfetchResponse } from '../../common/utils/parse_b_fetch'; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const retry = getService('retry'); const supertest = getService('legacySupertestAsApmReadUser'); @@ -45,7 +45,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { }; registry.when('failed transactions without data', { config: 'trial', archives: [] }, () => { - it('queries the search strategy and returns results', async () => { + it.skip('queries the search strategy and returns results', async () => { const intialResponse = await supertest .post(`/internal/bsearch`) .set('kbn-xsrf', 'foo') @@ -134,7 +134,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); registry.when('failed transactions with data', { config: 'trial', archives: ['8.0.0'] }, () => { - it('queries the search strategy and returns results', async () => { + it.skip('queries the search strategy and returns results', async () => { const intialResponse = await supertest .post(`/internal/bsearch`) .set('kbn-xsrf', 'foo') diff --git a/x-pack/test/apm_api_integration/tests/correlations/latency.ts b/x-pack/test/apm_api_integration/tests/correlations/latency.ts index 75a4edd447c70..8d768f559fb6d 100644 --- a/x-pack/test/apm_api_integration/tests/correlations/latency.ts +++ b/x-pack/test/apm_api_integration/tests/correlations/latency.ts @@ -14,10 +14,10 @@ import type { RawSearchStrategyClientParams } from '../../../../plugins/apm/comm import { APM_SEARCH_STRATEGIES } from '../../../../plugins/apm/common/search_strategies/constants'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; import { parseBfetchResponse } from '../../common/utils/parse_b_fetch'; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const retry = getService('retry'); const supertest = getService('legacySupertestAsApmReadUser'); @@ -144,7 +144,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { { config: 'trial', archives: ['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 () => { + it.skip('queries the search strategy and returns results', async () => { const intialResponse = await supertest .post(`/internal/bsearch`) .set('kbn-xsrf', 'foo') diff --git a/x-pack/test/apm_api_integration/tests/csm/csm_services.ts b/x-pack/test/apm_api_integration/tests/csm/csm_services.ts index 832ef93e3f721..2d5f0ddf1cd4c 100644 --- a/x-pack/test/apm_api_integration/tests/csm/csm_services.ts +++ b/x-pack/test/apm_api_integration/tests/csm/csm_services.ts @@ -7,9 +7,9 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; export default function rumServicesApiTests({ getService }: FtrProviderContext) { + const registry = getService('registry'); const supertest = getService('legacySupertestAsApmReadUser'); registry.when('CSM Services without data', { config: 'trial', archives: [] }, () => { diff --git a/x-pack/test/apm_api_integration/tests/csm/has_rum_data.ts b/x-pack/test/apm_api_integration/tests/csm/has_rum_data.ts index 3372e43396ed0..f9ba588ffccdb 100644 --- a/x-pack/test/apm_api_integration/tests/csm/has_rum_data.ts +++ b/x-pack/test/apm_api_integration/tests/csm/has_rum_data.ts @@ -7,9 +7,9 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; export default function rumHasDataApiTests({ getService }: FtrProviderContext) { + const registry = getService('registry'); const supertest = getService('legacySupertestAsApmReadUser'); registry.when('has_rum_data without data', { config: 'trial', archives: [] }, () => { diff --git a/x-pack/test/apm_api_integration/tests/csm/js_errors.ts b/x-pack/test/apm_api_integration/tests/csm/js_errors.ts index 6346c991373b5..5e4f306552273 100644 --- a/x-pack/test/apm_api_integration/tests/csm/js_errors.ts +++ b/x-pack/test/apm_api_integration/tests/csm/js_errors.ts @@ -7,9 +7,9 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; export default function rumJsErrorsApiTests({ getService }: FtrProviderContext) { + const registry = getService('registry'); const supertest = getService('legacySupertestAsApmReadUser'); registry.when('CSM JS errors with data', { config: 'trial', archives: [] }, () => { diff --git a/x-pack/test/apm_api_integration/tests/csm/long_task_metrics.ts b/x-pack/test/apm_api_integration/tests/csm/long_task_metrics.ts index 0cb84d1935fa8..ef1e537585b79 100644 --- a/x-pack/test/apm_api_integration/tests/csm/long_task_metrics.ts +++ b/x-pack/test/apm_api_integration/tests/csm/long_task_metrics.ts @@ -7,9 +7,9 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; export default function rumServicesApiTests({ getService }: FtrProviderContext) { + const registry = getService('registry'); const supertest = getService('legacySupertestAsApmReadUser'); registry.when('CSM long task metrics without data', { config: 'trial', archives: [] }, () => { diff --git a/x-pack/test/apm_api_integration/tests/csm/page_load_dist.ts b/x-pack/test/apm_api_integration/tests/csm/page_load_dist.ts index 8d6a38f27a8c4..1177b331c4c35 100644 --- a/x-pack/test/apm_api_integration/tests/csm/page_load_dist.ts +++ b/x-pack/test/apm_api_integration/tests/csm/page_load_dist.ts @@ -7,9 +7,9 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; export default function rumServicesApiTests({ getService }: FtrProviderContext) { + const registry = getService('registry'); const supertest = getService('legacySupertestAsApmReadUser'); registry.when('UX page load dist without data', { config: 'trial', archives: [] }, () => { diff --git a/x-pack/test/apm_api_integration/tests/csm/page_views.ts b/x-pack/test/apm_api_integration/tests/csm/page_views.ts index e5ffd37d3c682..40aa88aa5ad82 100644 --- a/x-pack/test/apm_api_integration/tests/csm/page_views.ts +++ b/x-pack/test/apm_api_integration/tests/csm/page_views.ts @@ -7,9 +7,9 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; export default function rumServicesApiTests({ getService }: FtrProviderContext) { + const registry = getService('registry'); const supertest = getService('legacySupertestAsApmReadUser'); registry.when('CSM page views without data', { config: 'trial', archives: [] }, () => { diff --git a/x-pack/test/apm_api_integration/tests/csm/url_search.ts b/x-pack/test/apm_api_integration/tests/csm/url_search.ts index 3c63186879788..f45e82865983e 100644 --- a/x-pack/test/apm_api_integration/tests/csm/url_search.ts +++ b/x-pack/test/apm_api_integration/tests/csm/url_search.ts @@ -7,9 +7,9 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; export default function rumServicesApiTests({ getService }: FtrProviderContext) { + const registry = getService('registry'); const supertest = getService('legacySupertestAsApmReadUser'); registry.when('CSM url search api without data', { config: 'trial', archives: [] }, () => { diff --git a/x-pack/test/apm_api_integration/tests/csm/web_core_vitals.ts b/x-pack/test/apm_api_integration/tests/csm/web_core_vitals.ts index 2c89b13d1b725..421bafcb4064f 100644 --- a/x-pack/test/apm_api_integration/tests/csm/web_core_vitals.ts +++ b/x-pack/test/apm_api_integration/tests/csm/web_core_vitals.ts @@ -7,9 +7,9 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; export default function rumServicesApiTests({ getService }: FtrProviderContext) { + const registry = getService('registry'); const supertest = getService('legacySupertestAsApmReadUser'); registry.when('CSM web core vitals without data', { config: 'trial', archives: [] }, () => { diff --git a/x-pack/test/apm_api_integration/tests/dependencies/metadata.ts b/x-pack/test/apm_api_integration/tests/dependencies/metadata.ts index 071370a8e6c73..e7092b31d6432 100644 --- a/x-pack/test/apm_api_integration/tests/dependencies/metadata.ts +++ b/x-pack/test/apm_api_integration/tests/dependencies/metadata.ts @@ -6,10 +6,10 @@ */ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; import { dataConfig, generateData } from './generate_data'; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); const synthtraceEsClient = getService('synthtraceEsClient'); diff --git a/x-pack/test/apm_api_integration/tests/dependencies/top_dependencies.ts b/x-pack/test/apm_api_integration/tests/dependencies/top_dependencies.ts index 6d32defed460d..d2ead8048358b 100644 --- a/x-pack/test/apm_api_integration/tests/dependencies/top_dependencies.ts +++ b/x-pack/test/apm_api_integration/tests/dependencies/top_dependencies.ts @@ -7,7 +7,6 @@ import expect from '@kbn/expect'; import { APIReturnType } from '../../../../plugins/apm/public/services/rest/createCallApmApi'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; import { dataConfig, generateData } from './generate_data'; import { NodeType, BackendNode } from '../../../../plugins/apm/common/connections'; import { roundNumber } from '../../utils'; @@ -15,6 +14,7 @@ import { roundNumber } from '../../utils'; type TopDependencies = APIReturnType<'GET /internal/apm/backends/top_backends'>; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); const synthtraceEsClient = getService('synthtraceEsClient'); diff --git a/x-pack/test/apm_api_integration/tests/error_rate/service_apis.ts b/x-pack/test/apm_api_integration/tests/error_rate/service_apis.ts index d5bf0bd21c7e8..7aca21f4fc7f6 100644 --- a/x-pack/test/apm_api_integration/tests/error_rate/service_apis.ts +++ b/x-pack/test/apm_api_integration/tests/error_rate/service_apis.ts @@ -11,9 +11,9 @@ import { LatencyAggregationType } from '../../../../plugins/apm/common/latency_a import { isFiniteNumber } from '../../../../plugins/apm/common/utils/is_finite_number'; import { PromiseReturnType } from '../../../../plugins/observability/typings/common'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); const synthtraceEsClient = getService('synthtraceEsClient'); diff --git a/x-pack/test/apm_api_integration/tests/errors/distribution.ts b/x-pack/test/apm_api_integration/tests/errors/distribution.ts index 487b5ff8a12c9..ed2cc468001f6 100644 --- a/x-pack/test/apm_api_integration/tests/errors/distribution.ts +++ b/x-pack/test/apm_api_integration/tests/errors/distribution.ts @@ -13,13 +13,13 @@ import { } from '../../../../plugins/apm/public/services/rest/createCallApmApi'; import { RecursivePartial } from '../../../../plugins/apm/typings/common'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; import { config, generateData } from './generate_data'; type ErrorsDistribution = APIReturnType<'GET /internal/apm/services/{serviceName}/errors/distribution'>; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); const synthtraceEsClient = getService('synthtraceEsClient'); diff --git a/x-pack/test/apm_api_integration/tests/errors/error_group_list.ts b/x-pack/test/apm_api_integration/tests/errors/error_group_list.ts index 4b5cbf4a2662a..5fc3f70d0d023 100644 --- a/x-pack/test/apm_api_integration/tests/errors/error_group_list.ts +++ b/x-pack/test/apm_api_integration/tests/errors/error_group_list.ts @@ -12,11 +12,11 @@ import { } from '../../../../plugins/apm/public/services/rest/createCallApmApi'; import { RecursivePartial } from '../../../../plugins/apm/typings/common'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; type ErrorGroups = APIReturnType<'GET /internal/apm/services/{serviceName}/errors'>['errorGroups']; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); const synthtraceEsClient = getService('synthtraceEsClient'); diff --git a/x-pack/test/apm_api_integration/tests/errors/group_id.ts b/x-pack/test/apm_api_integration/tests/errors/group_id.ts index ef9e293355a7f..9fc5165ed575e 100644 --- a/x-pack/test/apm_api_integration/tests/errors/group_id.ts +++ b/x-pack/test/apm_api_integration/tests/errors/group_id.ts @@ -11,13 +11,13 @@ import { } from '../../../../plugins/apm/public/services/rest/createCallApmApi'; import { RecursivePartial } from '../../../../plugins/apm/typings/common'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; import { config, generateData } from './generate_data'; type ErrorsDistribution = APIReturnType<'GET /internal/apm/services/{serviceName}/errors/{groupId}'>; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); const synthtraceEsClient = getService('synthtraceEsClient'); diff --git a/x-pack/test/apm_api_integration/tests/event_metadata/event_metadata.ts b/x-pack/test/apm_api_integration/tests/event_metadata/event_metadata.ts index fe461a94783a5..d1cc42a4c7c9d 100644 --- a/x-pack/test/apm_api_integration/tests/event_metadata/event_metadata.ts +++ b/x-pack/test/apm_api_integration/tests/event_metadata/event_metadata.ts @@ -8,9 +8,9 @@ import expect from '@kbn/expect'; import { PROCESSOR_EVENT } from '../../../../plugins/apm/common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../../plugins/apm/common/processor_event'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); const esClient = getService('es'); diff --git a/x-pack/test/apm_api_integration/tests/feature_controls.ts b/x-pack/test/apm_api_integration/tests/feature_controls.ts index ef3ba5d61fb54..f20aaaff2b23d 100644 --- a/x-pack/test/apm_api_integration/tests/feature_controls.ts +++ b/x-pack/test/apm_api_integration/tests/feature_controls.ts @@ -7,9 +7,9 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../common/ftr_provider_context'; -import { registry } from '../common/registry'; export default function featureControlsTests({ getService }: FtrProviderContext) { + const registry = getService('registry'); const supertest = getService('legacySupertestAsApmWriteUser'); const supertestWithoutAuth = getService('supertestWithoutAuth'); const security = getService('security'); diff --git a/x-pack/test/apm_api_integration/tests/historical_data/has_data.ts b/x-pack/test/apm_api_integration/tests/historical_data/has_data.ts index 70048c3d39210..e138742b25d4c 100644 --- a/x-pack/test/apm_api_integration/tests/historical_data/has_data.ts +++ b/x-pack/test/apm_api_integration/tests/historical_data/has_data.ts @@ -7,9 +7,9 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const supertest = getService('supertest'); const archiveName = 'apm_8.0.0'; diff --git a/x-pack/test/apm_api_integration/tests/index.ts b/x-pack/test/apm_api_integration/tests/index.ts index 46966834a176e..a4a58ed7bf5c2 100644 --- a/x-pack/test/apm_api_integration/tests/index.ts +++ b/x-pack/test/apm_api_integration/tests/index.ts @@ -6,10 +6,9 @@ */ import { FtrProviderContext } from '../common/ftr_provider_context'; -import { registry } from '../common/registry'; -export default function apmApiIntegrationTests(providerContext: FtrProviderContext) { - const { loadTestFile } = providerContext; +export default function apmApiIntegrationTests({ getService, loadTestFile }: FtrProviderContext) { + const registry = getService('registry'); describe('APM API tests', function () { this.tags('ciGroup1'); @@ -263,6 +262,6 @@ export default function apmApiIntegrationTests(providerContext: FtrProviderConte loadTestFile(require.resolve('./dependencies/top_dependencies')); }); - registry.run(providerContext); + registry.run(); }); } diff --git a/x-pack/test/apm_api_integration/tests/inspect/inspect.ts b/x-pack/test/apm_api_integration/tests/inspect/inspect.ts index a010c150124f0..443a03692aca9 100644 --- a/x-pack/test/apm_api_integration/tests/inspect/inspect.ts +++ b/x-pack/test/apm_api_integration/tests/inspect/inspect.ts @@ -7,11 +7,11 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; export default function customLinksTests({ getService }: FtrProviderContext) { + const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); const archiveName = 'apm_8.0.0'; @@ -49,7 +49,7 @@ export default function customLinksTests({ getService }: FtrProviderContext) { }, }); expect(status).to.be(200); - expect(body._inspect?.length).to.be(1); + expect(body._inspect).not.to.be.empty(); // @ts-expect-error expect(Object.keys(body._inspect[0])).to.eql([ diff --git a/x-pack/test/apm_api_integration/tests/latency/service_apis.ts b/x-pack/test/apm_api_integration/tests/latency/service_apis.ts index aa8282ccb0cc5..e87f03efeeefe 100644 --- a/x-pack/test/apm_api_integration/tests/latency/service_apis.ts +++ b/x-pack/test/apm_api_integration/tests/latency/service_apis.ts @@ -11,9 +11,9 @@ import { LatencyAggregationType } from '../../../../plugins/apm/common/latency_a import { isFiniteNumber } from '../../../../plugins/apm/common/utils/is_finite_number'; import { PromiseReturnType } from '../../../../plugins/observability/typings/common'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); const synthtraceEsClient = getService('synthtraceEsClient'); diff --git a/x-pack/test/apm_api_integration/tests/metrics_charts/metrics_charts.ts b/x-pack/test/apm_api_integration/tests/metrics_charts/metrics_charts.ts index 7b621de111ef8..74af2c2dba008 100644 --- a/x-pack/test/apm_api_integration/tests/metrics_charts/metrics_charts.ts +++ b/x-pack/test/apm_api_integration/tests/metrics_charts/metrics_charts.ts @@ -10,7 +10,6 @@ import { first } from 'lodash'; import { MetricsChartsByAgentAPIResponse } from '../../../../plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent'; import { GenericMetricsChart } from '../../../../plugins/apm/server/lib/metrics/transform_metrics_chart'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; interface ChartResponse { body: MetricsChartsByAgentAPIResponse; @@ -18,6 +17,7 @@ interface ChartResponse { } export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const supertest = getService('legacySupertestAsApmReadUser'); registry.when( diff --git a/x-pack/test/apm_api_integration/tests/observability_overview/has_data.ts b/x-pack/test/apm_api_integration/tests/observability_overview/has_data.ts index 90a01c472e13e..3aab948cd4f69 100644 --- a/x-pack/test/apm_api_integration/tests/observability_overview/has_data.ts +++ b/x-pack/test/apm_api_integration/tests/observability_overview/has_data.ts @@ -8,9 +8,9 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); registry.when( diff --git a/x-pack/test/apm_api_integration/tests/observability_overview/observability_overview.ts b/x-pack/test/apm_api_integration/tests/observability_overview/observability_overview.ts index 9082c5dec3b79..6b6d61fdb1d35 100644 --- a/x-pack/test/apm_api_integration/tests/observability_overview/observability_overview.ts +++ b/x-pack/test/apm_api_integration/tests/observability_overview/observability_overview.ts @@ -8,11 +8,11 @@ import { service, timerange } from '@elastic/apm-synthtrace'; import expect from '@kbn/expect'; import { meanBy, sumBy } from 'lodash'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; import { PromiseReturnType } from '../../../../plugins/observability/typings/common'; import { roundNumber } from '../../utils'; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); const synthtraceEsClient = getService('synthtraceEsClient'); diff --git a/x-pack/test/apm_api_integration/tests/service_maps/service_maps.ts b/x-pack/test/apm_api_integration/tests/service_maps/service_maps.ts index d4733785c62b8..bf607030c07d3 100644 --- a/x-pack/test/apm_api_integration/tests/service_maps/service_maps.ts +++ b/x-pack/test/apm_api_integration/tests/service_maps/service_maps.ts @@ -12,9 +12,9 @@ import { isEmpty, orderBy, uniq } from 'lodash'; import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; import { PromiseReturnType } from '../../../../plugins/observability/typings/common'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; export default function serviceMapsApiTests({ getService }: FtrProviderContext) { + const registry = getService('registry'); const supertest = getService('legacySupertestAsApmReadUser'); const supertestAsApmReadUserWithoutMlAccess = getService( 'legacySupertestAsApmReadUserWithoutMlAccess' diff --git a/x-pack/test/apm_api_integration/tests/service_overview/__snapshots__/instance_details.snap b/x-pack/test/apm_api_integration/tests/service_overview/__snapshots__/instance_details.snap index 8eb8ad2adb164..357b6111da67d 100644 --- a/x-pack/test/apm_api_integration/tests/service_overview/__snapshots__/instance_details.snap +++ b/x-pack/test/apm_api_integration/tests/service_overview/__snapshots__/instance_details.snap @@ -2,7 +2,6 @@ exports[`APM API tests basic apm_8.0.0 Instance details when data is loaded fetch instance details return the correct data 1`] = ` Object { - "@timestamp": "2021-08-03T06:57:50.204Z", "agent": Object { "ephemeral_id": "2745d454-f57f-4473-a09b-fe6bef295860", "name": "java", diff --git a/x-pack/test/apm_api_integration/tests/service_overview/dependencies/index.ts b/x-pack/test/apm_api_integration/tests/service_overview/dependencies/index.ts index 1549e403c1377..507b3d21e1b3f 100644 --- a/x-pack/test/apm_api_integration/tests/service_overview/dependencies/index.ts +++ b/x-pack/test/apm_api_integration/tests/service_overview/dependencies/index.ts @@ -17,10 +17,10 @@ import { import { APIReturnType } from '../../../../../plugins/apm/public/services/rest/createCallApmApi'; import archives from '../../../common/fixtures/es_archiver/archives_metadata'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; -import { registry } from '../../../common/registry'; import { apmDependenciesMapping, createServiceDependencyDocs } from './es_utils'; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); const es = getService('es'); diff --git a/x-pack/test/apm_api_integration/tests/service_overview/instance_details.ts b/x-pack/test/apm_api_integration/tests/service_overview/instance_details.ts index f3de8823199a8..04e32705efad9 100644 --- a/x-pack/test/apm_api_integration/tests/service_overview/instance_details.ts +++ b/x-pack/test/apm_api_integration/tests/service_overview/instance_details.ts @@ -6,9 +6,9 @@ */ import url from 'url'; import expect from '@kbn/expect'; +import { omit } from 'lodash'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import archives from '../../common/fixtures/es_archiver/archives_metadata'; -import { registry } from '../../common/registry'; import { APIReturnType } from '../../../../plugins/apm/public/services/rest/createCallApmApi'; import { getServiceNodeIds } from './get_service_node_ids'; import { createApmApiClient } from '../../common/apm_api_supertest'; @@ -17,6 +17,7 @@ type ServiceOverviewInstanceDetails = APIReturnType<'GET /internal/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}'>; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const supertest = getService('legacySupertestAsApmReadUser'); const apmApiSupertest = createApmApiClient(supertest); @@ -77,7 +78,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); it('return the correct data', () => { - expectSnapshot(response.body).toMatch(); + expectSnapshot(omit(response.body, '@timestamp')).toMatch(); }); }); } diff --git a/x-pack/test/apm_api_integration/tests/service_overview/instances_detailed_statistics.ts b/x-pack/test/apm_api_integration/tests/service_overview/instances_detailed_statistics.ts index 9d9fb6a221cf6..719b0b487172c 100644 --- a/x-pack/test/apm_api_integration/tests/service_overview/instances_detailed_statistics.ts +++ b/x-pack/test/apm_api_integration/tests/service_overview/instances_detailed_statistics.ts @@ -13,11 +13,11 @@ import { isFiniteNumber } from '../../../../plugins/apm/common/utils/is_finite_n import { APIReturnType } from '../../../../plugins/apm/public/services/rest/createCallApmApi'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import archives from '../../common/fixtures/es_archiver/archives_metadata'; -import { registry } from '../../common/registry'; import { createApmApiClient } from '../../common/apm_api_supertest'; import { getServiceNodeIds } from './get_service_node_ids'; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const supertest = getService('legacySupertestAsApmReadUser'); const apmApiSupertest = createApmApiClient(supertest); diff --git a/x-pack/test/apm_api_integration/tests/service_overview/instances_main_statistics.ts b/x-pack/test/apm_api_integration/tests/service_overview/instances_main_statistics.ts index 909e58a5566a5..5800ddf00480a 100644 --- a/x-pack/test/apm_api_integration/tests/service_overview/instances_main_statistics.ts +++ b/x-pack/test/apm_api_integration/tests/service_overview/instances_main_statistics.ts @@ -13,13 +13,13 @@ import { APIReturnType } from '../../../../plugins/apm/public/services/rest/crea import { isFiniteNumber } from '../../../../plugins/apm/common/utils/is_finite_number'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import archives from '../../common/fixtures/es_archiver/archives_metadata'; -import { registry } from '../../common/registry'; import { LatencyAggregationType } from '../../../../plugins/apm/common/latency_aggregation_types'; import { ENVIRONMENT_ALL } from '../../../../plugins/apm/common/environment_filter_values'; import { SERVICE_NODE_NAME_MISSING } from '../../../../plugins/apm/common/service_nodes'; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); const synthtraceEsClient = getService('synthtraceEsClient'); diff --git a/x-pack/test/apm_api_integration/tests/services/agent.ts b/x-pack/test/apm_api_integration/tests/services/agent.ts index fcd21f5a1a072..c1c1cc641c5fc 100644 --- a/x-pack/test/apm_api_integration/tests/services/agent.ts +++ b/x-pack/test/apm_api_integration/tests/services/agent.ts @@ -8,9 +8,9 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import archives from '../../common/fixtures/es_archiver/archives_metadata'; -import { registry } from '../../common/registry'; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const supertest = getService('legacySupertestAsApmReadUser'); const archiveName = 'apm_8.0.0'; diff --git a/x-pack/test/apm_api_integration/tests/services/annotations.ts b/x-pack/test/apm_api_integration/tests/services/annotations.ts index 25737ce8cce9a..2acfeac87880f 100644 --- a/x-pack/test/apm_api_integration/tests/services/annotations.ts +++ b/x-pack/test/apm_api_integration/tests/services/annotations.ts @@ -9,11 +9,11 @@ import expect from '@kbn/expect'; import { merge, cloneDeep, isPlainObject } from 'lodash'; import { JsonObject } from '@kbn/utility-types'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; const DEFAULT_INDEX_NAME = 'observability-annotations'; export default function annotationApiTests({ getService }: FtrProviderContext) { + const registry = getService('registry'); const supertestRead = getService('legacySupertestAsApmReadUser'); const supertestWrite = getService('legacySupertestAsApmAnnotationsWriteUser'); const es = getService('es'); diff --git a/x-pack/test/apm_api_integration/tests/services/derived_annotations.ts b/x-pack/test/apm_api_integration/tests/services/derived_annotations.ts index c9ee61557deb6..6a27f68be5d4f 100644 --- a/x-pack/test/apm_api_integration/tests/services/derived_annotations.ts +++ b/x-pack/test/apm_api_integration/tests/services/derived_annotations.ts @@ -9,9 +9,9 @@ import expect from '@kbn/expect'; import { APIReturnType } from '../../../../plugins/apm/public/services/rest/createCallApmApi'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; export default function annotationApiTests({ getService }: FtrProviderContext) { + const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); const es = getService('es'); diff --git a/x-pack/test/apm_api_integration/tests/services/error_groups/error_groups_detailed_statistics.ts b/x-pack/test/apm_api_integration/tests/services/error_groups/error_groups_detailed_statistics.ts index 54bbd4eb0bf71..e94de1d5410f1 100644 --- a/x-pack/test/apm_api_integration/tests/services/error_groups/error_groups_detailed_statistics.ts +++ b/x-pack/test/apm_api_integration/tests/services/error_groups/error_groups_detailed_statistics.ts @@ -15,7 +15,6 @@ import { } from '../../../../../plugins/apm/public/services/rest/createCallApmApi'; import { RecursivePartial } from '../../../../../plugins/apm/typings/common'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; -import { registry } from '../../../common/registry'; import { config, generateData } from './generate_data'; import { getErrorGroupIds } from './get_error_group_ids'; @@ -23,6 +22,7 @@ type ErrorGroupsDetailedStatistics = APIReturnType<'GET /internal/apm/services/{serviceName}/error_groups/detailed_statistics'>; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); const synthtraceEsClient = getService('synthtraceEsClient'); diff --git a/x-pack/test/apm_api_integration/tests/services/error_groups/error_groups_main_statistics.ts b/x-pack/test/apm_api_integration/tests/services/error_groups/error_groups_main_statistics.ts index bc6bd023a0f5e..3d8ddfe38cf5e 100644 --- a/x-pack/test/apm_api_integration/tests/services/error_groups/error_groups_main_statistics.ts +++ b/x-pack/test/apm_api_integration/tests/services/error_groups/error_groups_main_statistics.ts @@ -12,13 +12,13 @@ import { } from '../../../../../plugins/apm/public/services/rest/createCallApmApi'; import { RecursivePartial } from '../../../../../plugins/apm/typings/common'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; -import { registry } from '../../../common/registry'; import { generateData, config } from './generate_data'; type ErrorGroupsMainStatistics = APIReturnType<'GET /internal/apm/services/{serviceName}/error_groups/main_statistics'>; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); const synthtraceEsClient = getService('synthtraceEsClient'); diff --git a/x-pack/test/apm_api_integration/tests/services/service_details.ts b/x-pack/test/apm_api_integration/tests/services/service_details.ts index 28a3ee3ef82fd..0031569308224 100644 --- a/x-pack/test/apm_api_integration/tests/services/service_details.ts +++ b/x-pack/test/apm_api_integration/tests/services/service_details.ts @@ -9,9 +9,9 @@ import expect from '@kbn/expect'; import url from 'url'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import archives from '../../common/fixtures/es_archiver/archives_metadata'; -import { registry } from '../../common/registry'; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const supertest = getService('legacySupertestAsApmReadUser'); const archiveName = 'apm_8.0.0'; diff --git a/x-pack/test/apm_api_integration/tests/services/service_icons.ts b/x-pack/test/apm_api_integration/tests/services/service_icons.ts index e948c6981d6c6..f8b66a621e83e 100644 --- a/x-pack/test/apm_api_integration/tests/services/service_icons.ts +++ b/x-pack/test/apm_api_integration/tests/services/service_icons.ts @@ -9,9 +9,9 @@ import expect from '@kbn/expect'; import url from 'url'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import archives from '../../common/fixtures/es_archiver/archives_metadata'; -import { registry } from '../../common/registry'; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const supertest = getService('legacySupertestAsApmReadUser'); const archiveName = 'apm_8.0.0'; diff --git a/x-pack/test/apm_api_integration/tests/services/services_detailed_statistics.ts b/x-pack/test/apm_api_integration/tests/services/services_detailed_statistics.ts index d605c5f313825..742221d2a66e1 100644 --- a/x-pack/test/apm_api_integration/tests/services/services_detailed_statistics.ts +++ b/x-pack/test/apm_api_integration/tests/services/services_detailed_statistics.ts @@ -7,7 +7,6 @@ import expect from '@kbn/expect'; import url from 'url'; import moment from 'moment'; -import { registry } from '../../common/registry'; import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { APIReturnType } from '../../../../plugins/apm/public/services/rest/createCallApmApi'; @@ -17,6 +16,7 @@ type ServicesDetailedStatisticsReturn = APIReturnType<'GET /internal/apm/services/detailed_statistics'>; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const supertest = getService('legacySupertestAsApmReadUser'); const archiveName = 'apm_8.0.0'; diff --git a/x-pack/test/apm_api_integration/tests/services/throughput.ts b/x-pack/test/apm_api_integration/tests/services/throughput.ts index a9865a0c3bb38..077119156c641 100644 --- a/x-pack/test/apm_api_integration/tests/services/throughput.ts +++ b/x-pack/test/apm_api_integration/tests/services/throughput.ts @@ -16,12 +16,12 @@ import { } from '../../../../plugins/apm/public/services/rest/createCallApmApi'; import { RecursivePartial } from '../../../../plugins/apm/typings/common'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; import { roundNumber } from '../../utils'; type ThroughputReturn = APIReturnType<'GET /internal/apm/services/{serviceName}/throughput'>; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); const synthtraceEsClient = getService('synthtraceEsClient'); @@ -64,7 +64,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); registry.when( - 'data is loaded', + 'Throughput when data is loaded', { config: 'basic', archives: ['apm_mappings_only_8.0.0'] }, () => { describe('Throughput chart api', () => { diff --git a/x-pack/test/apm_api_integration/tests/services/top_services.ts b/x-pack/test/apm_api_integration/tests/services/top_services.ts index 42d98f38e4e4a..375206d0a0bc0 100644 --- a/x-pack/test/apm_api_integration/tests/services/top_services.ts +++ b/x-pack/test/apm_api_integration/tests/services/top_services.ts @@ -12,10 +12,10 @@ import { APIReturnType } from '../../../../plugins/apm/public/services/rest/crea import { PromiseReturnType } from '../../../../plugins/observability/typings/common'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; -import { registry } from '../../common/registry'; import { ENVIRONMENT_ALL } from '../../../../plugins/apm/common/environment_filter_values'; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const supertest = getService('legacySupertestAsApmReadUser'); const apmApiClient = getService('apmApiClient'); diff --git a/x-pack/test/apm_api_integration/tests/services/transaction_types.ts b/x-pack/test/apm_api_integration/tests/services/transaction_types.ts index 4b752971e82f8..82afa69029147 100644 --- a/x-pack/test/apm_api_integration/tests/services/transaction_types.ts +++ b/x-pack/test/apm_api_integration/tests/services/transaction_types.ts @@ -8,9 +8,9 @@ import expect from '@kbn/expect'; import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const supertest = getService('legacySupertestAsApmReadUser'); const archiveName = 'apm_8.0.0'; diff --git a/x-pack/test/apm_api_integration/tests/settings/agent_configuration.ts b/x-pack/test/apm_api_integration/tests/settings/agent_configuration.ts index 377c933144880..df504ec7444d7 100644 --- a/x-pack/test/apm_api_integration/tests/settings/agent_configuration.ts +++ b/x-pack/test/apm_api_integration/tests/settings/agent_configuration.ts @@ -5,15 +5,17 @@ * 2.0. */ +import { inspect } from 'util'; + import expect from '@kbn/expect'; import { omit, orderBy } from 'lodash'; import { AgentConfigurationIntake } from '../../../../plugins/apm/common/agent_configuration/configuration_types'; import { AgentConfigSearchParams } from '../../../../plugins/apm/server/routes/settings/agent_configuration'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; export default function agentConfigurationTests({ getService }: FtrProviderContext) { + const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); const log = getService('log'); @@ -133,19 +135,19 @@ export default function agentConfigurationTests({ getService }: FtrProviderConte it('can create and delete config', async () => { // assert that config does not exist - await expectStatusCode(() => searchConfigurations(searchParams), 404); + await expectMissing(() => searchConfigurations(searchParams)); // create config await createConfiguration(newConfig); // assert that config now exists - await expectStatusCode(() => searchConfigurations(searchParams), 200); + await expectExists(() => searchConfigurations(searchParams)); // delete config await deleteConfiguration(newConfig); // assert that config was deleted - await expectStatusCode(() => searchConfigurations(searchParams), 404); + await expectMissing(() => searchConfigurations(searchParams)); }); describe('when a configuration exists', () => { @@ -439,6 +441,16 @@ export default function agentConfigurationTests({ getService }: FtrProviderConte }); } ); + + async function expectExists(fn: () => ReturnType) { + const response = await fn(); + expect(response.body).not.to.be.empty(); + } + + async function expectMissing(fn: () => ReturnType) { + const response = await fn(); + expect(response.body).to.be.empty(); + } } async function waitFor(cb: () => Promise, retries = 50): Promise { @@ -464,10 +476,23 @@ async function expectStatusCode( }>, statusCode: number ) { + let res; try { - const res = await fn(); - expect(res.status).to.be(statusCode); + res = await fn(); } catch (e) { - expect(e.res.status).to.be(statusCode); + if (e && e.res && e.res.status) { + if (e.res.status === statusCode) { + return; + } + throw new Error( + `Expected a [${statusCode}] response, got [${e.res.status}]: ${inspect(e.res)}` + ); + } else { + throw new Error( + `Unexpected rejection value, expected error with .res response property: ${inspect(e)}` + ); + } } + + expect(res.status).to.be(statusCode); } diff --git a/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/basic.ts b/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/basic.ts index 7e05bd3faabfa..f7c35e92e08a0 100644 --- a/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/basic.ts +++ b/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/basic.ts @@ -6,10 +6,10 @@ */ import expect from '@kbn/expect'; -import { registry } from '../../../common/registry'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function apiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const noAccessUser = getService('legacySupertestAsNoAccessUser'); const readUser = getService('legacySupertestAsApmReadUser'); const writeUser = getService('legacySupertestAsApmWriteUser'); diff --git a/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/no_access_user.ts b/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/no_access_user.ts index 0b73b67c8e4c2..eefd5226ee7d0 100644 --- a/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/no_access_user.ts +++ b/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/no_access_user.ts @@ -6,10 +6,10 @@ */ import expect from '@kbn/expect'; -import { registry } from '../../../common/registry'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function apiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const noAccessUser = getService('legacySupertestAsNoAccessUser'); function getJobs() { diff --git a/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/read_user.ts b/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/read_user.ts index 982a47001f826..75967dc9a8ff9 100644 --- a/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/read_user.ts +++ b/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/read_user.ts @@ -6,10 +6,10 @@ */ import expect from '@kbn/expect'; -import { registry } from '../../../common/registry'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function apiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const apmReadUser = getService('legacySupertestAsApmReadUser'); function getJobs() { diff --git a/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/write_user.ts b/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/write_user.ts index 3d671c8fb825e..0bf4bec9ee8b1 100644 --- a/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/write_user.ts +++ b/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/write_user.ts @@ -7,10 +7,10 @@ import expect from '@kbn/expect'; import { countBy } from 'lodash'; -import { registry } from '../../../common/registry'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function apiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); const legacyWriteUserClient = getService('legacySupertestAsApmWriteUser'); diff --git a/x-pack/test/apm_api_integration/tests/settings/custom_link.ts b/x-pack/test/apm_api_integration/tests/settings/custom_link.ts index 86f60be3fdc4c..aeb110dc1faf2 100644 --- a/x-pack/test/apm_api_integration/tests/settings/custom_link.ts +++ b/x-pack/test/apm_api_integration/tests/settings/custom_link.ts @@ -8,10 +8,10 @@ import expect from '@kbn/expect'; import { CustomLink } from '../../../../plugins/apm/common/custom_link/custom_link_types'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; import { ApmApiError } from '../../common/apm_api_supertest'; export default function customLinksTests({ getService }: FtrProviderContext) { + const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); const log = getService('log'); diff --git a/x-pack/test/apm_api_integration/tests/suggestions/suggestions.ts b/x-pack/test/apm_api_integration/tests/suggestions/suggestions.ts index d551aec632fab..0cd16e5652f65 100644 --- a/x-pack/test/apm_api_integration/tests/suggestions/suggestions.ts +++ b/x-pack/test/apm_api_integration/tests/suggestions/suggestions.ts @@ -10,9 +10,9 @@ import { TRANSACTION_TYPE, } from '../../../../plugins/apm/common/elasticsearch_fieldnames'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; export default function suggestionsTests({ getService }: FtrProviderContext) { + const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); const archiveName = 'apm_8.0.0'; diff --git a/x-pack/test/apm_api_integration/tests/throughput/dependencies_apis.ts b/x-pack/test/apm_api_integration/tests/throughput/dependencies_apis.ts index 4cc9b2f0679fe..1b2c919f538a7 100644 --- a/x-pack/test/apm_api_integration/tests/throughput/dependencies_apis.ts +++ b/x-pack/test/apm_api_integration/tests/throughput/dependencies_apis.ts @@ -10,10 +10,10 @@ import { meanBy, sumBy } from 'lodash'; import { BackendNode, ServiceNode } from '../../../../plugins/apm/common/connections'; import { PromiseReturnType } from '../../../../plugins/observability/typings/common'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; import { roundNumber } from '../../utils'; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); const synthtraceEsClient = getService('synthtraceEsClient'); diff --git a/x-pack/test/apm_api_integration/tests/throughput/service_apis.ts b/x-pack/test/apm_api_integration/tests/throughput/service_apis.ts index c4fffd8d79afb..7318fc449fcdb 100644 --- a/x-pack/test/apm_api_integration/tests/throughput/service_apis.ts +++ b/x-pack/test/apm_api_integration/tests/throughput/service_apis.ts @@ -10,10 +10,10 @@ import { meanBy, sumBy } from 'lodash'; import { LatencyAggregationType } from '../../../../plugins/apm/common/latency_aggregation_types'; import { PromiseReturnType } from '../../../../plugins/observability/typings/common'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; import { roundNumber } from '../../utils'; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); const synthtraceEsClient = getService('synthtraceEsClient'); diff --git a/x-pack/test/apm_api_integration/tests/traces/top_traces.ts b/x-pack/test/apm_api_integration/tests/traces/top_traces.ts index 4968732f82203..51b14809982d8 100644 --- a/x-pack/test/apm_api_integration/tests/traces/top_traces.ts +++ b/x-pack/test/apm_api_integration/tests/traces/top_traces.ts @@ -9,9 +9,9 @@ import expect from '@kbn/expect'; import { sortBy } from 'lodash'; import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const supertest = getService('legacySupertestAsApmReadUser'); const archiveName = 'apm_8.0.0'; diff --git a/x-pack/test/apm_api_integration/tests/traces/trace_by_id.tsx b/x-pack/test/apm_api_integration/tests/traces/trace_by_id.tsx index d99a2b95a792e..085fa3a122b4c 100644 --- a/x-pack/test/apm_api_integration/tests/traces/trace_by_id.tsx +++ b/x-pack/test/apm_api_integration/tests/traces/trace_by_id.tsx @@ -8,10 +8,10 @@ import expect from '@kbn/expect'; import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; import { createApmApiClient, SupertestReturnType } from '../../common/apm_api_supertest'; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const supertest = getService('supertest'); const apmApiSupertest = createApmApiClient(supertest); diff --git a/x-pack/test/apm_api_integration/tests/transactions/breakdown.ts b/x-pack/test/apm_api_integration/tests/transactions/breakdown.ts index bc2e2f534a0bb..c24881b5c43f4 100644 --- a/x-pack/test/apm_api_integration/tests/transactions/breakdown.ts +++ b/x-pack/test/apm_api_integration/tests/transactions/breakdown.ts @@ -8,9 +8,9 @@ import expect from '@kbn/expect'; import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const supertest = getService('legacySupertestAsApmReadUser'); const archiveName = 'apm_8.0.0'; diff --git a/x-pack/test/apm_api_integration/tests/transactions/error_rate.ts b/x-pack/test/apm_api_integration/tests/transactions/error_rate.ts index aeb7cc61a582a..3a198d36e02e6 100644 --- a/x-pack/test/apm_api_integration/tests/transactions/error_rate.ts +++ b/x-pack/test/apm_api_integration/tests/transactions/error_rate.ts @@ -12,12 +12,12 @@ import moment from 'moment'; import { APIReturnType } from '../../../../plugins/apm/public/services/rest/createCallApmApi'; import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; type ErrorRate = APIReturnType<'GET /internal/apm/services/{serviceName}/transactions/charts/error_rate'>; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const supertest = getService('legacySupertestAsApmReadUser'); const archiveName = 'apm_8.0.0'; diff --git a/x-pack/test/apm_api_integration/tests/transactions/latency.ts b/x-pack/test/apm_api_integration/tests/transactions/latency.ts index beaff7647868a..6c69d09b45d62 100644 --- a/x-pack/test/apm_api_integration/tests/transactions/latency.ts +++ b/x-pack/test/apm_api_integration/tests/transactions/latency.ts @@ -12,12 +12,12 @@ import { APIReturnType } from '../../../../plugins/apm/public/services/rest/crea import { PromiseReturnType } from '../../../../plugins/observability/typings/common'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; -import { registry } from '../../common/registry'; type LatencyChartReturnType = APIReturnType<'GET /internal/apm/services/{serviceName}/transactions/charts/latency'>; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const supertest = getService('legacySupertestAsApmReadUser'); const archiveName = 'apm_8.0.0'; diff --git a/x-pack/test/apm_api_integration/tests/transactions/latency_overall_distribution.ts b/x-pack/test/apm_api_integration/tests/transactions/latency_overall_distribution.ts index b204f939020d7..ff1a49fb8fc54 100644 --- a/x-pack/test/apm_api_integration/tests/transactions/latency_overall_distribution.ts +++ b/x-pack/test/apm_api_integration/tests/transactions/latency_overall_distribution.ts @@ -7,9 +7,9 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); const endpoint = 'POST /internal/apm/latency/overall_distribution'; diff --git a/x-pack/test/apm_api_integration/tests/transactions/trace_samples.ts b/x-pack/test/apm_api_integration/tests/transactions/trace_samples.ts index 19e75ff08fec2..ef114740ec735 100644 --- a/x-pack/test/apm_api_integration/tests/transactions/trace_samples.ts +++ b/x-pack/test/apm_api_integration/tests/transactions/trace_samples.ts @@ -7,11 +7,13 @@ import expect from '@kbn/expect'; import qs from 'querystring'; +import { sortBy } from 'lodash'; +import { APIReturnType } from '../../../../plugins/apm/public/services/rest/createCallApmApi'; import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const supertest = getService('legacySupertestAsApmReadUser'); const archiveName = 'apm_8.0.0'; @@ -31,11 +33,13 @@ export default function ApiTest({ getService }: FtrProviderContext) { { config: 'basic', archives: [] }, () => { it('handles empty state', async () => { - const response = await supertest.get(url); + const response: { + body: APIReturnType<'GET /internal/apm/services/{serviceName}/transactions/traces/samples'>; + status: number; + } = await supertest.get(url); expect(response.status).to.be(200); - expect(response.body.noHits).to.be(true); expect(response.body.traceSamples.length).to.be(0); }); } @@ -45,14 +49,17 @@ export default function ApiTest({ getService }: FtrProviderContext) { 'Transaction trace samples response structure when data is loaded', { config: 'basic', archives: [archiveName] }, () => { - let response: any; + let response: { + body: APIReturnType<'GET /internal/apm/services/{serviceName}/transactions/traces/samples'>; + status: number; + }; + before(async () => { response = await supertest.get(url); }); it('returns the correct metadata', () => { expect(response.status).to.be(200); - expect(response.body.noHits).to.be(false); expect(response.body.traceSamples.length).to.be.greaterThan(0); }); @@ -63,43 +70,51 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('returns the correct samples', () => { const { traceSamples } = response.body; - expectSnapshot(traceSamples.sort((sample: any) => sample.traceId)).toMatchInline(` + expectSnapshot(sortBy(traceSamples, (sample) => sample.traceId)).toMatchInline(` Array [ Object { - "traceId": "5267685738bf75b68b16bf3426ba858c", - "transactionId": "5223f43bc3154c5a", + "traceId": "0996b09e42ad4dbfaaa6a069326c6e66", + "transactionId": "5721364b179716d0", }, Object { - "traceId": "9a84d15e5a0e32098d569948474e8e2f", - "transactionId": "b85db78a9824107b", + "traceId": "10d882b7118870015815a27c37892375", + "transactionId": "0cf9db0b1e321239", }, Object { - "traceId": "e123f0466fa092f345d047399db65aa2", - "transactionId": "c0af16286229d811", + "traceId": "2ca82e99453c58584c4b8de9a8ba4ec3", + "transactionId": "8fa2ca73976ce1e7", + }, + Object { + "traceId": "45b3d1a86003938687a55e49bf3610b8", + "transactionId": "a707456bda99ee98", }, Object { "traceId": "4943691f87b7eb97d442d1ef33ca65c7", "transactionId": "f6f4677d731e57c5", }, Object { - "traceId": "66bd97c457f5675665397ac9201cc050", - "transactionId": "592b60cc9ddabb15", + "traceId": "5267685738bf75b68b16bf3426ba858c", + "transactionId": "5223f43bc3154c5a", }, Object { - "traceId": "10d882b7118870015815a27c37892375", - "transactionId": "0cf9db0b1e321239", + "traceId": "66bd97c457f5675665397ac9201cc050", + "transactionId": "592b60cc9ddabb15", }, Object { "traceId": "6d85d8f1bc4bbbfdb19cdba59d2fc164", "transactionId": "d0a16f0f52f25d6b", }, Object { - "traceId": "0996b09e42ad4dbfaaa6a069326c6e66", - "transactionId": "5721364b179716d0", + "traceId": "7483bd52150d1c93a858c60bfdd0c138", + "transactionId": "e20e701ff93bdb55", }, Object { - "traceId": "d9415d102c0634e1e8fa53ceef07be70", - "transactionId": "fab91c68c9b1c42b", + "traceId": "9a84d15e5a0e32098d569948474e8e2f", + "transactionId": "b85db78a9824107b", + }, + Object { + "traceId": "a21ea39b41349a4614a86321d965c957", + "transactionId": "338bd7908cbf7f2d", }, Object { "traceId": "ca7a2072e7974ae84b5096706c6b6255", @@ -110,20 +125,12 @@ export default function ApiTest({ getService }: FtrProviderContext) { "transactionId": "6fcd12599c1b57fa", }, Object { - "traceId": "2ca82e99453c58584c4b8de9a8ba4ec3", - "transactionId": "8fa2ca73976ce1e7", - }, - Object { - "traceId": "45b3d1a86003938687a55e49bf3610b8", - "transactionId": "a707456bda99ee98", - }, - Object { - "traceId": "7483bd52150d1c93a858c60bfdd0c138", - "transactionId": "e20e701ff93bdb55", + "traceId": "d9415d102c0634e1e8fa53ceef07be70", + "transactionId": "fab91c68c9b1c42b", }, Object { - "traceId": "a21ea39b41349a4614a86321d965c957", - "transactionId": "338bd7908cbf7f2d", + "traceId": "e123f0466fa092f345d047399db65aa2", + "transactionId": "c0af16286229d811", }, ] `); diff --git a/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_detailed_statistics.ts b/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_detailed_statistics.ts index e877afc070050..72a0cdbbee48f 100644 --- a/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_detailed_statistics.ts +++ b/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_detailed_statistics.ts @@ -12,13 +12,13 @@ import { LatencyAggregationType } from '../../../../plugins/apm/common/latency_a import { asPercent } from '../../../../plugins/apm/common/utils/formatters'; import { APIReturnType } from '../../../../plugins/apm/public/services/rest/createCallApmApi'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; import { roundNumber } from '../../utils'; type TransactionsGroupsDetailedStatistics = APIReturnType<'GET /internal/apm/services/{serviceName}/transactions/groups/detailed_statistics'>; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); const synthtraceEsClient = getService('synthtraceEsClient'); diff --git a/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_main_statistics.ts b/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_main_statistics.ts index 7664d28271e14..2c645ce2c6277 100644 --- a/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_main_statistics.ts +++ b/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_main_statistics.ts @@ -11,12 +11,12 @@ import url from 'url'; import { APIReturnType } from '../../../../plugins/apm/public/services/rest/createCallApmApi'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import archives from '../../common/fixtures/es_archiver/archives_metadata'; -import { registry } from '../../common/registry'; type TransactionsGroupsPrimaryStatistics = APIReturnType<'GET /internal/apm/services/{serviceName}/transactions/groups/main_statistics'>; export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); const supertest = getService('legacySupertestAsApmReadUser'); const archiveName = 'apm_8.0.0'; diff --git a/x-pack/test/fleet_api_integration/apis/package_policy/upgrade.ts b/x-pack/test/fleet_api_integration/apis/package_policy/upgrade.ts index a61a77fd37f6b..5a61d69ed8ba6 100644 --- a/x-pack/test/fleet_api_integration/apis/package_policy/upgrade.ts +++ b/x-pack/test/fleet_api_integration/apis/package_policy/upgrade.ts @@ -570,16 +570,14 @@ export default function (providerContext: FtrProviderContext) { describe('upgrade', function () { it('fails to upgrade package policy', async function () { - const { body }: { body: UpgradePackagePolicyResponse } = await supertest + await supertest .post(`/api/fleet/package_policies/upgrade`) .set('kbn-xsrf', 'xxxx') .send({ packagePolicyIds: [packagePolicyId], dryRun: false, }) - .expect(200); - - expect(body[0].success).to.be(false); + .expect(400); }); }); }); @@ -672,16 +670,14 @@ export default function (providerContext: FtrProviderContext) { describe('upgrade', function () { it('fails to upgrade package policy', async function () { - const { body }: { body: UpgradePackagePolicyResponse } = await supertest + await supertest .post(`/api/fleet/package_policies/upgrade`) .set('kbn-xsrf', 'xxxx') .send({ packagePolicyIds: [packagePolicyId], dryRun: false, }) - .expect(200); - - expect(body[0].success).to.be(false); + .expect(400); }); }); }); diff --git a/x-pack/test/functional/apps/discover/value_suggestions_non_timebased.ts b/x-pack/test/functional/apps/discover/value_suggestions_non_timebased.ts index e8cc34604eaba..ecf8fd31ce932 100644 --- a/x-pack/test/functional/apps/discover/value_suggestions_non_timebased.ts +++ b/x-pack/test/functional/apps/discover/value_suggestions_non_timebased.ts @@ -11,10 +11,10 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const queryBar = getService('queryBar'); - const PageObjects = getPageObjects(['common', 'settings', 'context']); + const retry = getService('retry'); + const PageObjects = getPageObjects(['common', 'settings', 'context', 'header']); - // FLAKY: https://github.com/elastic/kibana/issues/114745 - describe.skip('value suggestions non time based', function describeIndexTests() { + describe('value suggestions non time based', function describeIndexTests() { before(async function () { await esArchiver.loadIfNeeded( 'test/functional/fixtures/es_archiver/index_pattern_without_timefield' @@ -29,11 +29,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('shows all autosuggest options for a filter in discover context app', async () => { await PageObjects.common.navigateToApp('discover'); - await queryBar.setQuery('type.keyword : '); - const suggestions = await queryBar.getSuggestions(); - expect(suggestions.length).to.be(1); - expect(suggestions).to.contain('"apache"'); + + await retry.try(async () => { + const suggestions = await queryBar.getSuggestions(); + expect(suggestions.length).to.be(1); + expect(suggestions).to.contain('"apache"'); + }); }); }); } diff --git a/x-pack/test/functional/services/ml/trained_models_table.ts b/x-pack/test/functional/services/ml/trained_models_table.ts index 11a97a4fed8fe..dc1749ae15ac6 100644 --- a/x-pack/test/functional/services/ml/trained_models_table.ts +++ b/x-pack/test/functional/services/ml/trained_models_table.ts @@ -146,8 +146,8 @@ export function TrainedModelsTableProvider({ getService }: FtrProviderContext) { // 'Created at' will be different on each run, // so we will just assert that the value is in the expected timestamp format. expect(modelRow.createdAt).to.match( - /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/, - `Expected trained model row created at time to have same format as '2019-12-05 12:28:34' (got '${modelRow.createdAt}')` + /^\w{3}\s\d+,\s\d{4}\s@\s\d{2}:\d{2}:\d{2}\.\d{3}$/, + `Expected trained model row created at time to have same format as 'Dec 5, 2019 @ 12:28:34.594' (got '${modelRow.createdAt}')` ); }