diff --git a/.buildkite/scripts/steps/functional/serverless_ftr.sh b/.buildkite/scripts/steps/functional/serverless_ftr.sh index ba75fe6034cae..72a69897e54e3 100755 --- a/.buildkite/scripts/steps/functional/serverless_ftr.sh +++ b/.buildkite/scripts/steps/functional/serverless_ftr.sh @@ -28,6 +28,7 @@ elif [[ "$SERVERLESS_ENVIRONMENT" == "security" ]]; then fi EXIT_CODE=0 +OFFENDING_CONFIG= for CONFIG in "${SERVERLESS_CONFIGS[@]}" do @@ -42,7 +43,17 @@ do if [ $LAST_CODE -ne 0 ]; then EXIT_CODE=10 + OFFENDING_CONFIG=$CONFIG fi done +echo "--- Serverless FTR Results for $JOB" +if [ $EXIT_CODE -eq 0 ]; then + echo "✅ Success!" +elif [ $EXIT_CODE -eq 10 ]; then + echo "❌ Failed in config: $OFFENDING_CONFIG, exit code set to 10 for soft-failure" +else + echo "❌ Failed." +fi + exit $EXIT_CODE diff --git a/.eslintrc.js b/.eslintrc.js index ddd39ed00747a..e1153546e8154 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -984,6 +984,7 @@ module.exports = { // front end and common typescript and javascript files only files: [ 'x-pack/plugins/ecs_data_quality_dashboard/common/**/*.{js,mjs,ts,tsx}', + 'x-pack/plugins/elastic_assistant/common/**/*.{js,mjs,ts,tsx}', 'x-pack/packages/kbn-elastic-assistant/**/*.{js,mjs,ts,tsx}', 'x-pack/packages/security-solution/**/*.{js,mjs,ts,tsx}', 'x-pack/plugins/security_solution/public/**/*.{js,mjs,ts,tsx}', @@ -1016,6 +1017,7 @@ module.exports = { // This should be a very small set as most linter rules are useful for tests as well. files: [ 'x-pack/plugins/ecs_data_quality_dashboard/**/*.{ts,tsx}', + 'x-pack/plugins/elastic_assistant/**/*.{ts,tsx}', 'x-pack/packages/kbn-elastic-assistant/**/*.{ts,tsx}', 'x-pack/packages/security-solution/**/*.{ts,tsx}', 'x-pack/plugins/security_solution/**/*.{ts,tsx}', @@ -1026,6 +1028,7 @@ module.exports = { ], excludedFiles: [ 'x-pack/plugins/ecs_data_quality_dashboard/**/*.{test,mock,test_helper}.{ts,tsx}', + 'x-pack/plugins/elastic_assistant/**/*.{test,mock,test_helper}.{ts,tsx}', 'x-pack/packages/kbn-elastic-assistant/**/*.{test,mock,test_helper}.{ts,tsx}', 'x-pack/packages/security-solution/**/*.{test,mock,test_helper}.{ts,tsx}', 'x-pack/plugins/security_solution/**/*.{test,mock,test_helper}.{ts,tsx}', @@ -1042,6 +1045,7 @@ module.exports = { // typescript only for front and back end files: [ 'x-pack/plugins/ecs_data_quality_dashboard/**/*.{ts,tsx}', + 'x-pack/plugins/elastic_assistant/**/*.{ts,tsx}', 'x-pack/packages/kbn-elastic-assistant/**/*.{ts,tsx}', 'x-pack/packages/security-solution/**/*.{ts,tsx}', 'x-pack/plugins/security_solution/**/*.{ts,tsx}', @@ -1077,6 +1081,7 @@ module.exports = { // typescript and javascript for front and back end files: [ 'x-pack/plugins/ecs_data_quality_dashboard/**/*.{js,mjs,ts,tsx}', + 'x-pack/plugins/elastic_assistant/**/*.{js,mjs,ts,tsx}', 'x-pack/packages/kbn-elastic-assistant/**/*.{js,mjs,ts,tsx}', 'x-pack/packages/security-solution/**/*.{js,mjs,ts,tsx}', 'x-pack/plugins/security_solution/**/*.{js,mjs,ts,tsx}', diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 911bfb5161dc0..4f050e3bf422f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -340,6 +340,7 @@ packages/kbn-ecs @elastic/kibana-core @elastic/security-threat-hunting-investiga x-pack/packages/security-solution/ecs_data_quality_dashboard @elastic/security-threat-hunting-investigations x-pack/plugins/ecs_data_quality_dashboard @elastic/security-threat-hunting-investigations x-pack/packages/kbn-elastic-assistant @elastic/security-solution +x-pack/plugins/elastic_assistant @elastic/security-solution test/plugin_functional/plugins/elasticsearch_client_plugin @elastic/kibana-core x-pack/test/plugin_api_integration/plugins/elasticsearch_client @elastic/kibana-core x-pack/plugins/embeddable_enhanced @elastic/kibana-presentation @@ -517,6 +518,7 @@ x-pack/plugins/monitoring @elastic/infra-monitoring-ui src/plugins/navigation @elastic/appex-sharedux src/plugins/newsfeed @elastic/kibana-core test/common/plugins/newsfeed @elastic/kibana-core +src/plugins/no_data_page @elastic/appex-sharedux x-pack/plugins/notifications @elastic/appex-sharedux packages/kbn-object-versioning @elastic/appex-sharedux x-pack/plugins/observability_ai_assistant @elastic/obs-ai-assistant @@ -824,6 +826,12 @@ packages/kbn-yarn-lock-validator @elastic/kibana-operations /x-pack/test/stack_functional_integration/apps/ccs/ccs_discover.js @elastic/kibana-data-discovery /x-pack/test/stack_functional_integration/apps/management/_index_pattern_create.js @elastic/kibana-data-discovery /x-pack/test/upgrade/apps/discover @elastic/kibana-data-discovery +/x-pack/test_serverless/api_integration/test_suites/common/data_views @elastic/kibana-data-discovery +/x-pack/test_serverless/api_integration/test_suites/common/data_view_field_editor @elastic/kibana-data-discovery +/x-pack/test_serverless/api_integration/test_suites/common/kql_telemetry @elastic/kibana-data-discovery +/x-pack/test_serverless/api_integration/test_suites/common/scripts_tests @elastic/kibana-data-discovery +/x-pack/test_serverless/api_integration/test_suites/common/search_oss @elastic/kibana-data-discovery +/x-pack/test_serverless/api_integration/test_suites/common/search_xpack @elastic/kibana-data-discovery # Visualizations /src/plugins/visualize/ @elastic/kibana-visualizations diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index e57392132e528..e377ceae177bc 100644 --- a/api_docs/actions.mdx +++ b/api_docs/actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/actions title: "actions" image: https://source.unsplash.com/400x175/?github description: API docs for the actions plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'actions'] --- import actionsObj from './actions.devdocs.json'; diff --git a/api_docs/advanced_settings.mdx b/api_docs/advanced_settings.mdx index 15f233cc9b6c3..a3c2ae0b512d6 100644 --- a/api_docs/advanced_settings.mdx +++ b/api_docs/advanced_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/advancedSettings title: "advancedSettings" image: https://source.unsplash.com/400x175/?github description: API docs for the advancedSettings plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index 8d05b3d90abda..a8df73deaa4e4 100644 --- a/api_docs/aiops.mdx +++ b/api_docs/aiops.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiops title: "aiops" image: https://source.unsplash.com/400x175/?github description: API docs for the aiops plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiops'] --- import aiopsObj from './aiops.devdocs.json'; diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index 9a5b2ab1194ac..dc0e5a6b0c78d 100644 --- a/api_docs/alerting.mdx +++ b/api_docs/alerting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/alerting title: "alerting" image: https://source.unsplash.com/400x175/?github description: API docs for the alerting plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'alerting'] --- import alertingObj from './alerting.devdocs.json'; diff --git a/api_docs/apm.devdocs.json b/api_docs/apm.devdocs.json index 28779a9fc58a8..05aa88a397978 100644 --- a/api_docs/apm.devdocs.json +++ b/api_docs/apm.devdocs.json @@ -408,7 +408,7 @@ "label": "APIEndpoint", "description": [], "signature": [ - "\"POST /internal/apm/data_view/static\" | \"GET /internal/apm/data_view/title\" | \"GET /internal/apm/environments\" | \"GET /internal/apm/services/{serviceName}/errors/groups/main_statistics\" | \"GET /internal/apm/services/{serviceName}/errors/groups/main_statistics_by_transaction_name\" | \"POST /internal/apm/services/{serviceName}/errors/groups/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/errors/{groupId}/samples\" | \"GET /internal/apm/services/{serviceName}/errors/{groupId}/error/{errorId}\" | \"GET /internal/apm/services/{serviceName}/errors/distribution\" | \"GET /internal/apm/services/{serviceName}/errors/{groupId}/top_erroneous_transactions\" | \"POST /internal/apm/latency/overall_distribution/transactions\" | \"GET /internal/apm/services/{serviceName}/metrics/charts\" | \"GET /internal/apm/services/{serviceName}/metrics/nodes\" | \"GET /internal/apm/services/{serviceName}/metrics/serverless/charts\" | \"GET /internal/apm/services/{serviceName}/metrics/serverless/summary\" | \"GET /internal/apm/services/{serviceName}/metrics/serverless/functions_overview\" | \"GET /internal/apm/services/{serviceName}/metrics/serverless/active_instances\" | \"GET /internal/apm/observability_overview\" | \"GET /internal/apm/observability_overview/has_data\" | \"GET /internal/apm/service-map\" | \"GET /internal/apm/service-map/service/{serviceName}\" | \"GET /internal/apm/service-map/dependency\" | \"GET /internal/apm/services\" | \"POST /internal/apm/services/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/metadata/details\" | \"GET /internal/apm/services/{serviceName}/metadata/icons\" | \"GET /internal/apm/services/{serviceName}/agent\" | \"GET /internal/apm/services/{serviceName}/transaction_types\" | \"GET /internal/apm/services/{serviceName}/node/{serviceNodeName}/metadata\" | \"GET /api/apm/services/{serviceName}/annotation/search 2023-10-31\" | \"POST /api/apm/services/{serviceName}/annotation 2023-10-31\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}\" | \"GET /internal/apm/services/{serviceName}/throughput\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/dependencies\" | \"GET /internal/apm/services/{serviceName}/dependencies/breakdown\" | \"GET /internal/apm/services/{serviceName}/anomaly_charts\" | \"GET /internal/apm/services/{serviceName}/alerts_count\" | \"GET /internal/apm/service-groups\" | \"GET /internal/apm/service-group\" | \"POST /internal/apm/service-group\" | \"DELETE /internal/apm/service-group\" | \"GET /internal/apm/service-group/services\" | \"GET /internal/apm/service-group/counts\" | \"GET /internal/apm/suggestions\" | \"GET /internal/apm/traces/{traceId}\" | \"GET /internal/apm/traces\" | \"GET /internal/apm/traces/{traceId}/root_transaction\" | \"GET /internal/apm/transactions/{transactionId}\" | \"GET /internal/apm/traces/find\" | \"POST /internal/apm/traces/aggregated_critical_path\" | \"GET /internal/apm/traces/{traceId}/transactions/{transactionId}\" | \"GET /internal/apm/traces/{traceId}/spans/{spanId}\" | \"GET /internal/apm/services/{serviceName}/transactions/groups/main_statistics\" | \"GET /internal/apm/services/{serviceName}/transactions/groups/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/latency\" | \"GET /internal/apm/services/{serviceName}/transactions/traces/samples\" | \"GET /internal/apm/services/{serviceName}/transaction/charts/breakdown\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/error_rate\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/coldstart_rate\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/coldstart_rate_by_transaction_name\" | \"GET /internal/apm/rule_types/transaction_error_rate/chart_preview\" | \"GET /internal/apm/rule_types/error_count/chart_preview\" | \"GET /internal/apm/rule_types/transaction_duration/chart_preview\" | \"GET /api/apm/settings/agent-configuration 2023-10-31\" | \"GET /api/apm/settings/agent-configuration/view 2023-10-31\" | \"DELETE /api/apm/settings/agent-configuration 2023-10-31\" | \"PUT /api/apm/settings/agent-configuration 2023-10-31\" | \"POST /api/apm/settings/agent-configuration/search 2023-10-31\" | \"GET /api/apm/settings/agent-configuration/environments 2023-10-31\" | \"GET /api/apm/settings/agent-configuration/agent_name 2023-10-31\" | \"GET /internal/apm/settings/anomaly-detection/jobs\" | \"POST /internal/apm/settings/anomaly-detection/jobs\" | \"GET /internal/apm/settings/anomaly-detection/environments\" | \"POST /internal/apm/settings/anomaly-detection/update_to_v3\" | \"GET /internal/apm/settings/apm-index-settings\" | \"GET /internal/apm/settings/apm-indices\" | \"POST /internal/apm/settings/apm-indices/save\" | \"GET /internal/apm/settings/custom_links/transaction\" | \"GET /internal/apm/settings/custom_links\" | \"POST /internal/apm/settings/custom_links\" | \"PUT /internal/apm/settings/custom_links/{id}\" | \"DELETE /internal/apm/settings/custom_links/{id}\" | \"GET /api/apm/sourcemaps 2023-10-31\" | \"POST /api/apm/sourcemaps 2023-10-31\" | \"DELETE /api/apm/sourcemaps/{id} 2023-10-31\" | \"POST /api/apm/androidmaps 2023-10-31\" | \"POST /internal/apm/sourcemaps/migrate_fleet_artifacts\" | \"GET /internal/apm/fleet/has_apm_policies\" | \"GET /internal/apm/fleet/agents\" | \"POST /api/apm/fleet/apm_server_schema 2023-10-31\" | \"GET /internal/apm/fleet/apm_server_schema/unsupported\" | \"GET /internal/apm/fleet/migration_check\" | \"POST /internal/apm/fleet/cloud_apm_package_policy\" | \"GET /internal/apm/fleet/java_agent_versions\" | \"GET /internal/apm/dependencies/top_dependencies\" | \"GET /internal/apm/dependencies/upstream_services\" | \"GET /internal/apm/dependencies/metadata\" | \"GET /internal/apm/dependencies/charts/latency\" | \"GET /internal/apm/dependencies/charts/throughput\" | \"GET /internal/apm/dependencies/charts/error_rate\" | \"GET /internal/apm/dependencies/operations\" | \"GET /internal/apm/dependencies/charts/distribution\" | \"GET /internal/apm/dependencies/operations/spans\" | \"GET /internal/apm/correlations/field_candidates/transactions\" | \"GET /internal/apm/correlations/field_value_stats/transactions\" | \"POST /internal/apm/correlations/field_value_pairs/transactions\" | \"POST /internal/apm/correlations/significant_correlations/transactions\" | \"POST /internal/apm/correlations/p_values/transactions\" | \"GET /internal/apm/fallback_to_transactions\" | \"GET /internal/apm/has_data\" | \"GET /internal/apm/event_metadata/{processorEvent}/{id}\" | \"GET /internal/apm/agent_keys\" | \"GET /internal/apm/agent_keys/privileges\" | \"POST /internal/apm/api_key/invalidate\" | \"POST /api/apm/agent_keys 2023-10-31\" | \"GET /internal/apm/storage_explorer\" | \"GET /internal/apm/services/{serviceName}/storage_details\" | \"GET /internal/apm/storage_chart\" | \"GET /internal/apm/storage_explorer/privileges\" | \"GET /internal/apm/storage_explorer_summary_stats\" | \"GET /internal/apm/storage_explorer/is_cross_cluster_search\" | \"GET /internal/apm/storage_explorer/get_services\" | \"GET /internal/apm/traces/{traceId}/span_links/{spanId}/parents\" | \"GET /internal/apm/traces/{traceId}/span_links/{spanId}/children\" | \"GET /internal/apm/services/{serviceName}/infrastructure_attributes\" | \"GET /internal/apm/debug-telemetry\" | \"GET /internal/apm/time_range_metadata\" | \"GET /internal/apm/settings/labs\" | \"GET /internal/apm/get_agents_per_service\" | \"GET /internal/apm/get_latest_agent_versions\" | \"GET /internal/apm/services/{serviceName}/agent_instances\" | \"GET /internal/apm/services/{serviceName}/mobile/filters\" | \"GET /internal/apm/mobile-services/{serviceName}/most_used_charts\" | \"GET /internal/apm/mobile-services/{serviceName}/transactions/charts/sessions\" | \"GET /internal/apm/mobile-services/{serviceName}/transactions/charts/http_requests\" | \"GET /internal/apm/mobile-services/{serviceName}/stats\" | \"GET /internal/apm/mobile-services/{serviceName}/location/stats\" | \"GET /internal/apm/mobile-services/{serviceName}/terms\" | \"GET /internal/apm/mobile-services/{serviceName}/main_statistics\" | \"GET /internal/apm/mobile-services/{serviceName}/detailed_statistics\" | \"GET /internal/apm/diagnostics\" | \"POST /internal/apm/assistant/get_apm_timeseries\" | \"GET /internal/apm/assistant/get_service_summary\" | \"GET /internal/apm/assistant/get_error_document\" | \"POST /internal/apm/assistant/get_correlation_values\" | \"GET /internal/apm/assistant/get_downstream_dependencies\"" + "\"POST /internal/apm/data_view/static\" | \"GET /internal/apm/data_view/title\" | \"GET /internal/apm/environments\" | \"GET /internal/apm/services/{serviceName}/errors/groups/main_statistics\" | \"GET /internal/apm/services/{serviceName}/errors/groups/main_statistics_by_transaction_name\" | \"POST /internal/apm/services/{serviceName}/errors/groups/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/errors/{groupId}/samples\" | \"GET /internal/apm/services/{serviceName}/errors/{groupId}/error/{errorId}\" | \"GET /internal/apm/services/{serviceName}/errors/distribution\" | \"GET /internal/apm/services/{serviceName}/errors/{groupId}/top_erroneous_transactions\" | \"POST /internal/apm/latency/overall_distribution/transactions\" | \"GET /internal/apm/services/{serviceName}/metrics/charts\" | \"GET /internal/apm/services/{serviceName}/metrics/nodes\" | \"GET /internal/apm/services/{serviceName}/metrics/serverless/charts\" | \"GET /internal/apm/services/{serviceName}/metrics/serverless/summary\" | \"GET /internal/apm/services/{serviceName}/metrics/serverless/functions_overview\" | \"GET /internal/apm/services/{serviceName}/metrics/serverless/active_instances\" | \"GET /internal/apm/observability_overview\" | \"GET /internal/apm/observability_overview/has_data\" | \"GET /internal/apm/service-map\" | \"GET /internal/apm/service-map/service/{serviceName}\" | \"GET /internal/apm/service-map/dependency\" | \"GET /internal/apm/services\" | \"POST /internal/apm/services/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/metadata/details\" | \"GET /internal/apm/services/{serviceName}/metadata/icons\" | \"GET /internal/apm/services/{serviceName}/agent\" | \"GET /internal/apm/services/{serviceName}/transaction_types\" | \"GET /internal/apm/services/{serviceName}/node/{serviceNodeName}/metadata\" | \"GET /api/apm/services/{serviceName}/annotation/search 2023-10-31\" | \"POST /api/apm/services/{serviceName}/annotation 2023-10-31\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}\" | \"GET /internal/apm/services/{serviceName}/throughput\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/dependencies\" | \"GET /internal/apm/services/{serviceName}/dependencies/breakdown\" | \"GET /internal/apm/services/{serviceName}/anomaly_charts\" | \"GET /internal/apm/services/{serviceName}/alerts_count\" | \"GET /internal/apm/service-groups\" | \"GET /internal/apm/service-group\" | \"POST /internal/apm/service-group\" | \"DELETE /internal/apm/service-group\" | \"GET /internal/apm/service-group/services\" | \"GET /internal/apm/service-group/counts\" | \"GET /internal/apm/suggestions\" | \"GET /internal/apm/traces/{traceId}\" | \"GET /internal/apm/traces\" | \"GET /internal/apm/traces/{traceId}/root_transaction\" | \"GET /internal/apm/transactions/{transactionId}\" | \"GET /internal/apm/traces/find\" | \"POST /internal/apm/traces/aggregated_critical_path\" | \"GET /internal/apm/traces/{traceId}/transactions/{transactionId}\" | \"GET /internal/apm/traces/{traceId}/spans/{spanId}\" | \"GET /internal/apm/services/{serviceName}/transactions/groups/main_statistics\" | \"GET /internal/apm/services/{serviceName}/transactions/groups/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/latency\" | \"GET /internal/apm/services/{serviceName}/transactions/traces/samples\" | \"GET /internal/apm/services/{serviceName}/transaction/charts/breakdown\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/error_rate\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/coldstart_rate\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/coldstart_rate_by_transaction_name\" | \"GET /internal/apm/rule_types/transaction_error_rate/chart_preview\" | \"GET /internal/apm/rule_types/error_count/chart_preview\" | \"GET /internal/apm/rule_types/transaction_duration/chart_preview\" | \"GET /api/apm/settings/agent-configuration 2023-10-31\" | \"GET /api/apm/settings/agent-configuration/view 2023-10-31\" | \"DELETE /api/apm/settings/agent-configuration 2023-10-31\" | \"PUT /api/apm/settings/agent-configuration 2023-10-31\" | \"POST /api/apm/settings/agent-configuration/search 2023-10-31\" | \"GET /api/apm/settings/agent-configuration/environments 2023-10-31\" | \"GET /api/apm/settings/agent-configuration/agent_name 2023-10-31\" | \"GET /internal/apm/settings/anomaly-detection/jobs\" | \"POST /internal/apm/settings/anomaly-detection/jobs\" | \"GET /internal/apm/settings/anomaly-detection/environments\" | \"POST /internal/apm/settings/anomaly-detection/update_to_v3\" | \"GET /internal/apm/settings/apm-index-settings\" | \"GET /internal/apm/settings/apm-indices\" | \"POST /internal/apm/settings/apm-indices/save\" | \"GET /internal/apm/settings/custom_links/transaction\" | \"GET /internal/apm/settings/custom_links\" | \"POST /internal/apm/settings/custom_links\" | \"PUT /internal/apm/settings/custom_links/{id}\" | \"DELETE /internal/apm/settings/custom_links/{id}\" | \"GET /api/apm/sourcemaps 2023-10-31\" | \"POST /api/apm/sourcemaps 2023-10-31\" | \"DELETE /api/apm/sourcemaps/{id} 2023-10-31\" | \"POST /api/apm/androidmaps 2023-10-31\" | \"POST /internal/apm/sourcemaps/migrate_fleet_artifacts\" | \"GET /internal/apm/fleet/has_apm_policies\" | \"GET /internal/apm/fleet/agents\" | \"POST /api/apm/fleet/apm_server_schema 2023-10-31\" | \"GET /internal/apm/fleet/apm_server_schema/unsupported\" | \"GET /internal/apm/fleet/migration_check\" | \"POST /internal/apm/fleet/cloud_apm_package_policy\" | \"GET /internal/apm/fleet/java_agent_versions\" | \"GET /internal/apm/dependencies/top_dependencies\" | \"GET /internal/apm/dependencies/upstream_services\" | \"GET /internal/apm/dependencies/metadata\" | \"GET /internal/apm/dependencies/charts/latency\" | \"GET /internal/apm/dependencies/charts/throughput\" | \"GET /internal/apm/dependencies/charts/error_rate\" | \"GET /internal/apm/dependencies/operations\" | \"GET /internal/apm/dependencies/charts/distribution\" | \"GET /internal/apm/dependencies/operations/spans\" | \"GET /internal/apm/correlations/field_candidates/transactions\" | \"GET /internal/apm/correlations/field_value_stats/transactions\" | \"POST /internal/apm/correlations/field_value_pairs/transactions\" | \"POST /internal/apm/correlations/significant_correlations/transactions\" | \"POST /internal/apm/correlations/p_values/transactions\" | \"GET /internal/apm/fallback_to_transactions\" | \"GET /internal/apm/has_data\" | \"GET /internal/apm/event_metadata/{processorEvent}/{id}\" | \"GET /internal/apm/agent_keys\" | \"GET /internal/apm/agent_keys/privileges\" | \"POST /internal/apm/api_key/invalidate\" | \"POST /api/apm/agent_keys 2023-10-31\" | \"GET /internal/apm/storage_explorer\" | \"GET /internal/apm/services/{serviceName}/storage_details\" | \"GET /internal/apm/storage_chart\" | \"GET /internal/apm/storage_explorer/privileges\" | \"GET /internal/apm/storage_explorer_summary_stats\" | \"GET /internal/apm/storage_explorer/is_cross_cluster_search\" | \"GET /internal/apm/storage_explorer/get_services\" | \"GET /internal/apm/traces/{traceId}/span_links/{spanId}/parents\" | \"GET /internal/apm/traces/{traceId}/span_links/{spanId}/children\" | \"GET /internal/apm/services/{serviceName}/infrastructure_attributes\" | \"GET /internal/apm/debug-telemetry\" | \"GET /internal/apm/time_range_metadata\" | \"GET /internal/apm/settings/labs\" | \"GET /internal/apm/get_agents_per_service\" | \"GET /internal/apm/get_latest_agent_versions\" | \"GET /internal/apm/services/{serviceName}/agent_instances\" | \"GET /internal/apm/services/{serviceName}/mobile/filters\" | \"GET /internal/apm/mobile-services/{serviceName}/most_used_charts\" | \"GET /internal/apm/mobile-services/{serviceName}/transactions/charts/sessions\" | \"GET /internal/apm/mobile-services/{serviceName}/transactions/charts/http_requests\" | \"GET /internal/apm/mobile-services/{serviceName}/stats\" | \"GET /internal/apm/mobile-services/{serviceName}/location/stats\" | \"GET /internal/apm/mobile-services/{serviceName}/terms\" | \"GET /internal/apm/mobile-services/{serviceName}/main_statistics\" | \"GET /internal/apm/mobile-services/{serviceName}/detailed_statistics\" | \"GET /internal/apm/diagnostics\" | \"POST /internal/apm/assistant/get_apm_timeseries\" | \"GET /internal/apm/assistant/get_service_summary\" | \"GET /internal/apm/assistant/get_error_document\" | \"POST /internal/apm/assistant/get_correlation_values\" | \"GET /internal/apm/assistant/get_downstream_dependencies\" | \"GET /internal/apm/assistant/get_services_list\"" ], "path": "x-pack/plugins/apm/server/routes/apm_routes/get_global_apm_server_route_repository.ts", "deprecated": false, @@ -455,7 +455,51 @@ "label": "APMServerRouteRepository", "description": [], "signature": [ - "{ \"GET /internal/apm/assistant/get_downstream_dependencies\": { endpoint: \"GET /internal/apm/assistant/get_downstream_dependencies\"; params?: ", + "{ \"GET /internal/apm/assistant/get_services_list\": { endpoint: \"GET /internal/apm/assistant/get_services_list\"; params?: ", + "TypeC", + "<{ query: ", + "IntersectionC", + "<[", + "TypeC", + "<{ start: ", + "StringC", + "; end: ", + "StringC", + "; }>, ", + "PartialC", + "<{ 'service.environment': ", + "UnionC", + "<[", + "LiteralC", + "<\"ENVIRONMENT_NOT_DEFINED\">, ", + "LiteralC", + "<\"ENVIRONMENT_ALL\">, ", + "BrandC", + "<", + "StringC", + ", ", + { + "pluginId": "@kbn/io-ts-utils", + "scope": "common", + "docId": "kibKbnIoTsUtilsPluginApi", + "section": "def-common.NonEmptyStringBrand", + "text": "NonEmptyStringBrand" + }, + ">]>; }>]>; }> | undefined; handler: ({}: ", + "APMRouteHandlerResources", + " & { params: { query: { start: string; end: string; } & { 'service.environment'?: \"ENVIRONMENT_NOT_DEFINED\" | \"ENVIRONMENT_ALL\" | ", + "Branded", + " | undefined; }; }; }) => Promise<{ content: ApmServicesListContent; }>; } & ", + "APMRouteCreateOptions", + "; \"GET /internal/apm/assistant/get_downstream_dependencies\": { endpoint: \"GET /internal/apm/assistant/get_downstream_dependencies\"; params?: ", "TypeC", "<{ query: ", "IntersectionC", @@ -807,7 +851,7 @@ "IngestGetPipelineResponse", " | undefined; }; diagnosticsPrivileges: { index: Record; cluster: Record; hasAllClusterPrivileges: boolean; hasAllIndexPrivileges: boolean; hasAllPrivileges: boolean; }; apmIndices: Readonly<{} & { error: string; metric: string; transaction: string; span: string; onboarding: string; }>; apmIndexTemplates: { name: string; isNonStandard: boolean; exists: boolean; }[]; fleetPackageInfo: { isInstalled: boolean; version?: string | undefined; }; kibanaVersion: string; elasticsearchVersion: string; apmEvents: ", + ">; cluster: Record; hasAllClusterPrivileges: boolean; hasAllIndexPrivileges: boolean; hasAllPrivileges: boolean; }; apmIndices: Readonly<{} & { error: string; metric: string; transaction: string; span: string; onboarding: string; sourcemap: string; }>; apmIndexTemplates: { name: string; isNonStandard: boolean; exists: boolean; }[]; fleetPackageInfo: { isInstalled: boolean; version?: string | undefined; }; kibanaVersion: string; elasticsearchVersion: string; apmEvents: ", "ApmEvent", "[]; invalidIndices?: ", "IndiciesItem", @@ -3329,7 +3373,7 @@ "PartialC", "; }> | undefined; handler: ({}: ", "APMRouteHandlerResources", - " & { params: { body: { readonly error?: string | undefined; readonly metric?: string | undefined; readonly transaction?: string | undefined; readonly span?: string | undefined; readonly onboarding?: string | undefined; }; }; }) => Promise<", + " & { params: { body: { readonly error?: string | undefined; readonly metric?: string | undefined; readonly transaction?: string | undefined; readonly span?: string | undefined; readonly onboarding?: string | undefined; readonly sourcemap?: string | undefined; }; }; }) => Promise<", { "pluginId": "@kbn/core-saved-objects-common", "scope": "common", @@ -3341,7 +3385,7 @@ "APMRouteCreateOptions", "; \"GET /internal/apm/settings/apm-indices\": { endpoint: \"GET /internal/apm/settings/apm-indices\"; params?: undefined; handler: ({}: ", "APMRouteHandlerResources", - ") => Promise>; } & ", + ") => Promise>; } & ", "APMRouteCreateOptions", "; \"GET /internal/apm/settings/apm-index-settings\": { endpoint: \"GET /internal/apm/settings/apm-index-settings\"; params?: undefined; handler: ({}: ", "APMRouteHandlerResources", diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index 310fbc2e88b68..b2bff86331a71 100644 --- a/api_docs/apm.mdx +++ b/api_docs/apm.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apm title: "apm" image: https://source.unsplash.com/400x175/?github description: API docs for the apm plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/apm_data_access.devdocs.json b/api_docs/apm_data_access.devdocs.json index aaab84fef1ef1..57fe58d681f92 100644 --- a/api_docs/apm_data_access.devdocs.json +++ b/api_docs/apm_data_access.devdocs.json @@ -22,7 +22,7 @@ "label": "APMDataAccessConfig", "description": [], "signature": [ - "{ readonly indices: Readonly<{} & { error: string; metric: string; transaction: string; span: string; onboarding: string; }>; }" + "{ readonly indices: Readonly<{} & { error: string; metric: string; transaction: string; span: string; onboarding: string; sourcemap: string; }>; }" ], "path": "x-pack/plugins/apm_data_access/server/index.ts", "deprecated": false, @@ -37,7 +37,7 @@ "label": "APMIndices", "description": [], "signature": [ - "{ readonly error: string; readonly metric: string; readonly transaction: string; readonly span: string; readonly onboarding: string; }" + "{ readonly error: string; readonly metric: string; readonly transaction: string; readonly span: string; readonly onboarding: string; readonly sourcemap: string; }" ], "path": "x-pack/plugins/apm_data_access/server/index.ts", "deprecated": false, @@ -65,7 +65,7 @@ "label": "apmIndicesFromConfigFile", "description": [], "signature": [ - "{ readonly error: string; readonly metric: string; readonly transaction: string; readonly span: string; readonly onboarding: string; }" + "{ readonly error: string; readonly metric: string; readonly transaction: string; readonly span: string; readonly onboarding: string; readonly sourcemap: string; }" ], "path": "x-pack/plugins/apm_data_access/server/types.ts", "deprecated": false, @@ -87,7 +87,7 @@ "section": "def-common.SavedObjectsClientContract", "text": "SavedObjectsClientContract" }, - ") => Promise>" + ") => Promise>" ], "path": "x-pack/plugins/apm_data_access/server/types.ts", "deprecated": false, diff --git a/api_docs/apm_data_access.mdx b/api_docs/apm_data_access.mdx index 3aa0167513dc7..7f9d6eb4d6979 100644 --- a/api_docs/apm_data_access.mdx +++ b/api_docs/apm_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apmDataAccess title: "apmDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the apmDataAccess plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apmDataAccess'] --- import apmDataAccessObj from './apm_data_access.devdocs.json'; diff --git a/api_docs/asset_manager.mdx b/api_docs/asset_manager.mdx index bd66eb290ba60..b3aea5514a082 100644 --- a/api_docs/asset_manager.mdx +++ b/api_docs/asset_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/assetManager title: "assetManager" image: https://source.unsplash.com/400x175/?github description: API docs for the assetManager plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'assetManager'] --- import assetManagerObj from './asset_manager.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index 1cd3d916be7e3..1e40fdd64b159 100644 --- a/api_docs/banners.mdx +++ b/api_docs/banners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/banners title: "banners" image: https://source.unsplash.com/400x175/?github description: API docs for the banners plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'banners'] --- import bannersObj from './banners.devdocs.json'; diff --git a/api_docs/bfetch.mdx b/api_docs/bfetch.mdx index cd3621bef2e16..83a8cc40306b6 100644 --- a/api_docs/bfetch.mdx +++ b/api_docs/bfetch.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/bfetch title: "bfetch" image: https://source.unsplash.com/400x175/?github description: API docs for the bfetch plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'bfetch'] --- import bfetchObj from './bfetch.devdocs.json'; diff --git a/api_docs/canvas.mdx b/api_docs/canvas.mdx index 9a2fab273e554..4f65b84fa40f2 100644 --- a/api_docs/canvas.mdx +++ b/api_docs/canvas.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/canvas title: "canvas" image: https://source.unsplash.com/400x175/?github description: API docs for the canvas plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'canvas'] --- import canvasObj from './canvas.devdocs.json'; diff --git a/api_docs/cases.mdx b/api_docs/cases.mdx index a0c3e54b744d4..cfaf4b7b3056d 100644 --- a/api_docs/cases.mdx +++ b/api_docs/cases.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cases title: "cases" image: https://source.unsplash.com/400x175/?github description: API docs for the cases plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cases'] --- import casesObj from './cases.devdocs.json'; diff --git a/api_docs/charts.mdx b/api_docs/charts.mdx index 8b081708de4b1..613b1af2ac128 100644 --- a/api_docs/charts.mdx +++ b/api_docs/charts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/charts title: "charts" image: https://source.unsplash.com/400x175/?github description: API docs for the charts plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'charts'] --- import chartsObj from './charts.devdocs.json'; diff --git a/api_docs/cloud.mdx b/api_docs/cloud.mdx index 01458d8a111a4..c65150369cd92 100644 --- a/api_docs/cloud.mdx +++ b/api_docs/cloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloud title: "cloud" image: https://source.unsplash.com/400x175/?github description: API docs for the cloud plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_chat.mdx b/api_docs/cloud_chat.mdx index 1eab55e866256..89d22b4cadf85 100644 --- a/api_docs/cloud_chat.mdx +++ b/api_docs/cloud_chat.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudChat title: "cloudChat" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudChat plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudChat'] --- import cloudChatObj from './cloud_chat.devdocs.json'; diff --git a/api_docs/cloud_chat_provider.mdx b/api_docs/cloud_chat_provider.mdx index 0ff6f8f1b8622..497715c6452dc 100644 --- a/api_docs/cloud_chat_provider.mdx +++ b/api_docs/cloud_chat_provider.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudChatProvider title: "cloudChatProvider" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudChatProvider plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudChatProvider'] --- import cloudChatProviderObj from './cloud_chat_provider.devdocs.json'; diff --git a/api_docs/cloud_data_migration.mdx b/api_docs/cloud_data_migration.mdx index c6549a2b2dfcc..0220ebe6fce30 100644 --- a/api_docs/cloud_data_migration.mdx +++ b/api_docs/cloud_data_migration.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDataMigration title: "cloudDataMigration" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDataMigration plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDataMigration'] --- import cloudDataMigrationObj from './cloud_data_migration.devdocs.json'; diff --git a/api_docs/cloud_defend.mdx b/api_docs/cloud_defend.mdx index 1b5a9f56b8ab7..5daa2dc23ed19 100644 --- a/api_docs/cloud_defend.mdx +++ b/api_docs/cloud_defend.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDefend title: "cloudDefend" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDefend plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDefend'] --- import cloudDefendObj from './cloud_defend.devdocs.json'; diff --git a/api_docs/cloud_experiments.mdx b/api_docs/cloud_experiments.mdx index 49fb366ba59b3..73671acb55813 100644 --- a/api_docs/cloud_experiments.mdx +++ b/api_docs/cloud_experiments.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudExperiments title: "cloudExperiments" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudExperiments plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudExperiments'] --- import cloudExperimentsObj from './cloud_experiments.devdocs.json'; diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index c54377c2c7b01..5f74e4dd1dccb 100644 --- a/api_docs/cloud_security_posture.mdx +++ b/api_docs/cloud_security_posture.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudSecurityPosture title: "cloudSecurityPosture" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudSecurityPosture plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudSecurityPosture'] --- import cloudSecurityPostureObj from './cloud_security_posture.devdocs.json'; diff --git a/api_docs/console.mdx b/api_docs/console.mdx index 6a5205f95b8ad..e03f7d1496f1a 100644 --- a/api_docs/console.mdx +++ b/api_docs/console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/console title: "console" image: https://source.unsplash.com/400x175/?github description: API docs for the console plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] --- import consoleObj from './console.devdocs.json'; diff --git a/api_docs/content_management.mdx b/api_docs/content_management.mdx index d8a8087c9dd2a..a9b98bbe50122 100644 --- a/api_docs/content_management.mdx +++ b/api_docs/content_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/contentManagement title: "contentManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the contentManagement plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'contentManagement'] --- import contentManagementObj from './content_management.devdocs.json'; diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index 9e00843c9cd9f..180b52069b3cd 100644 --- a/api_docs/controls.mdx +++ b/api_docs/controls.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/controls title: "controls" image: https://source.unsplash.com/400x175/?github description: API docs for the controls plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls'] --- import controlsObj from './controls.devdocs.json'; diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index 110fd493b3f67..883d17498c404 100644 --- a/api_docs/custom_integrations.mdx +++ b/api_docs/custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/customIntegrations title: "customIntegrations" image: https://source.unsplash.com/400x175/?github description: API docs for the customIntegrations plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'customIntegrations'] --- import customIntegrationsObj from './custom_integrations.devdocs.json'; diff --git a/api_docs/dashboard.mdx b/api_docs/dashboard.mdx index ec3769adff5ef..bdd762edacbc5 100644 --- a/api_docs/dashboard.mdx +++ b/api_docs/dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboard title: "dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboard plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboard'] --- import dashboardObj from './dashboard.devdocs.json'; diff --git a/api_docs/dashboard_enhanced.mdx b/api_docs/dashboard_enhanced.mdx index 457355f3f47be..7eae636681801 100644 --- a/api_docs/dashboard_enhanced.mdx +++ b/api_docs/dashboard_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboardEnhanced title: "dashboardEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboardEnhanced plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardEnhanced'] --- import dashboardEnhancedObj from './dashboard_enhanced.devdocs.json'; diff --git a/api_docs/data.devdocs.json b/api_docs/data.devdocs.json index bee45b5ee444a..17cf390695f9e 100644 --- a/api_docs/data.devdocs.json +++ b/api_docs/data.devdocs.json @@ -13890,11 +13890,11 @@ }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx" + "path": "x-pack/plugins/security_solution/public/flyout/preview/components/rule_preview.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/flyout/preview/components/rule_preview.tsx" + "path": "x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx" }, { "plugin": "transform", @@ -21655,11 +21655,11 @@ }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx" + "path": "x-pack/plugins/security_solution/public/flyout/preview/components/rule_preview.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/flyout/preview/components/rule_preview.tsx" + "path": "x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx" }, { "plugin": "transform", diff --git a/api_docs/data.mdx b/api_docs/data.mdx index 80a8e41aa1824..3173f22684c86 100644 --- a/api_docs/data.mdx +++ b/api_docs/data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data title: "data" image: https://source.unsplash.com/400x175/?github description: API docs for the data plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] --- import dataObj from './data.devdocs.json'; diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index 4af0c115d5164..4610c901d482c 100644 --- a/api_docs/data_query.mdx +++ b/api_docs/data_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-query title: "data.query" image: https://source.unsplash.com/400x175/?github description: API docs for the data.query plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.query'] --- import dataQueryObj from './data_query.devdocs.json'; diff --git a/api_docs/data_search.devdocs.json b/api_docs/data_search.devdocs.json index 97b33f8d418ea..3b5cd7151721e 100644 --- a/api_docs/data_search.devdocs.json +++ b/api_docs/data_search.devdocs.json @@ -3154,7 +3154,7 @@ "section": "def-common.SavedObjectsFindOptionsReference", "text": "SavedObjectsFindOptionsReference" }, - "[] | undefined; hasNoReferenceOperator?: \"AND\" | \"OR\" | undefined; typeToNamespacesMap?: Map | undefined; migrationVersionCompatibility?: \"raw\" | \"compatible\" | undefined; downwardConversion?: \"allow\" | \"forbid\" | undefined; }" + "[] | undefined; hasNoReferenceOperator?: \"AND\" | \"OR\" | undefined; typeToNamespacesMap?: Map | undefined; migrationVersionCompatibility?: \"raw\" | \"compatible\" | undefined; }" ], "path": "src/plugins/data/server/search/session/types.ts", "deprecated": false, diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index 22e1172947cfd..da269e2ad8258 100644 --- a/api_docs/data_search.mdx +++ b/api_docs/data_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-search title: "data.search" image: https://source.unsplash.com/400x175/?github description: API docs for the data.search plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] --- import dataSearchObj from './data_search.devdocs.json'; diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index 1d42bd4d19fa6..0eadf9e93ada3 100644 --- a/api_docs/data_view_editor.mdx +++ b/api_docs/data_view_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewEditor title: "dataViewEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewEditor plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewEditor'] --- import dataViewEditorObj from './data_view_editor.devdocs.json'; diff --git a/api_docs/data_view_field_editor.mdx b/api_docs/data_view_field_editor.mdx index 7d63985712cef..0f3fd5fc08760 100644 --- a/api_docs/data_view_field_editor.mdx +++ b/api_docs/data_view_field_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewFieldEditor title: "dataViewFieldEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewFieldEditor plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewFieldEditor'] --- import dataViewFieldEditorObj from './data_view_field_editor.devdocs.json'; diff --git a/api_docs/data_view_management.mdx b/api_docs/data_view_management.mdx index 587767cffbd5b..15c25492cdafa 100644 --- a/api_docs/data_view_management.mdx +++ b/api_docs/data_view_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewManagement title: "dataViewManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewManagement plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewManagement'] --- import dataViewManagementObj from './data_view_management.devdocs.json'; diff --git a/api_docs/data_views.devdocs.json b/api_docs/data_views.devdocs.json index 3ac18c3d9e79a..4a845a6057727 100644 --- a/api_docs/data_views.devdocs.json +++ b/api_docs/data_views.devdocs.json @@ -649,11 +649,11 @@ }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx" + "path": "x-pack/plugins/security_solution/public/flyout/preview/components/rule_preview.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/flyout/preview/components/rule_preview.tsx" + "path": "x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx" }, { "plugin": "transform", @@ -8613,11 +8613,11 @@ }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx" + "path": "x-pack/plugins/security_solution/public/flyout/preview/components/rule_preview.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/flyout/preview/components/rule_preview.tsx" + "path": "x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx" }, { "plugin": "transform", @@ -15687,11 +15687,11 @@ }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx" + "path": "x-pack/plugins/security_solution/public/flyout/preview/components/rule_preview.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/flyout/preview/components/rule_preview.tsx" + "path": "x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx" }, { "plugin": "transform", diff --git a/api_docs/data_views.mdx b/api_docs/data_views.mdx index adfb5e2df5f9b..96bf7eb84fd99 100644 --- a/api_docs/data_views.mdx +++ b/api_docs/data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViews title: "dataViews" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViews plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViews'] --- import dataViewsObj from './data_views.devdocs.json'; diff --git a/api_docs/data_visualizer.mdx b/api_docs/data_visualizer.mdx index 31a7a8d8a03d3..b67f64a5f94a1 100644 --- a/api_docs/data_visualizer.mdx +++ b/api_docs/data_visualizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataVisualizer title: "dataVisualizer" image: https://source.unsplash.com/400x175/?github description: API docs for the dataVisualizer plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] --- import dataVisualizerObj from './data_visualizer.devdocs.json'; diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index e133bf0f0ce6d..e5c6f8f7f98bb 100644 --- a/api_docs/deprecations_by_api.mdx +++ b/api_docs/deprecations_by_api.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByApi slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-api title: Deprecated API usage by API description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index b8e4335461b42..d6a24b5788ad9 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByPlugin slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-plugin title: Deprecated API usage by plugin description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index ee53150242ea9..4d9206678e728 100644 --- a/api_docs/deprecations_by_team.mdx +++ b/api_docs/deprecations_by_team.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsDueByTeam slug: /kibana-dev-docs/api-meta/deprecations-due-by-team title: Deprecated APIs due to be removed, by team description: Lists the teams that are referencing deprecated APIs with a remove by date. -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index 27bb91fe6cf56..df3eec5b6c75f 100644 --- a/api_docs/dev_tools.mdx +++ b/api_docs/dev_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/devTools title: "devTools" image: https://source.unsplash.com/400x175/?github description: API docs for the devTools plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'devTools'] --- import devToolsObj from './dev_tools.devdocs.json'; diff --git a/api_docs/discover.mdx b/api_docs/discover.mdx index 0037157559554..9251bed21f4e0 100644 --- a/api_docs/discover.mdx +++ b/api_docs/discover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discover title: "discover" image: https://source.unsplash.com/400x175/?github description: API docs for the discover plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discover'] --- import discoverObj from './discover.devdocs.json'; diff --git a/api_docs/discover_enhanced.mdx b/api_docs/discover_enhanced.mdx index 524afafefe26b..f7f82769628b9 100644 --- a/api_docs/discover_enhanced.mdx +++ b/api_docs/discover_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverEnhanced title: "discoverEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverEnhanced plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/ecs_data_quality_dashboard.mdx b/api_docs/ecs_data_quality_dashboard.mdx index 25c602bcc81d9..f7dae10eb09b4 100644 --- a/api_docs/ecs_data_quality_dashboard.mdx +++ b/api_docs/ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ecsDataQualityDashboard title: "ecsDataQualityDashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the ecsDataQualityDashboard plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ecsDataQualityDashboard'] --- import ecsDataQualityDashboardObj from './ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/elastic_assistant.devdocs.json b/api_docs/elastic_assistant.devdocs.json new file mode 100644 index 0000000000000..9a3dd4987b4a5 --- /dev/null +++ b/api_docs/elastic_assistant.devdocs.json @@ -0,0 +1,101 @@ +{ + "id": "elasticAssistant", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [], + "setup": { + "parentPluginId": "elasticAssistant", + "id": "def-server.ElasticAssistantPluginSetup", + "type": "Interface", + "tags": [], + "label": "ElasticAssistantPluginSetup", + "description": [ + "The plugin setup interface" + ], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "elasticAssistant", + "id": "def-server.ElasticAssistantPluginSetup.actions", + "type": "Object", + "tags": [], + "label": "actions", + "description": [], + "signature": [ + { + "pluginId": "actions", + "scope": "server", + "docId": "kibActionsPluginApi", + "section": "def-server.PluginSetupContract", + "text": "PluginSetupContract" + } + ], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "lifecycle": "setup", + "initialIsOpen": true + }, + "start": { + "parentPluginId": "elasticAssistant", + "id": "def-server.ElasticAssistantPluginStart", + "type": "Interface", + "tags": [], + "label": "ElasticAssistantPluginStart", + "description": [ + "The plugin start interface" + ], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "elasticAssistant", + "id": "def-server.ElasticAssistantPluginStart.actions", + "type": "Object", + "tags": [], + "label": "actions", + "description": [], + "signature": [ + { + "pluginId": "actions", + "scope": "server", + "docId": "kibActionsPluginApi", + "section": "def-server.PluginStartContract", + "text": "PluginStartContract" + } + ], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "lifecycle": "start", + "initialIsOpen": true + } + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/elastic_assistant.mdx b/api_docs/elastic_assistant.mdx new file mode 100644 index 0000000000000..b54c2cf5e5cac --- /dev/null +++ b/api_docs/elastic_assistant.mdx @@ -0,0 +1,33 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibElasticAssistantPluginApi +slug: /kibana-dev-docs/api/elasticAssistant +title: "elasticAssistant" +image: https://source.unsplash.com/400x175/?github +description: API docs for the elasticAssistant plugin +date: 2023-08-29 +tags: ['contributor', 'dev', 'apidocs', 'kibana', 'elasticAssistant'] +--- +import elasticAssistantObj from './elastic_assistant.devdocs.json'; + +Server APIs for the Elastic AI Assistant + +Contact [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 4 | 0 | 2 | 0 | + +## Server + +### Setup + + +### Start + + diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index c3265dcee0706..3833618281f9c 100644 --- a/api_docs/embeddable.mdx +++ b/api_docs/embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddable title: "embeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddable plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddable'] --- import embeddableObj from './embeddable.devdocs.json'; diff --git a/api_docs/embeddable_enhanced.mdx b/api_docs/embeddable_enhanced.mdx index 915053c2392bb..a35dd8b2a6f39 100644 --- a/api_docs/embeddable_enhanced.mdx +++ b/api_docs/embeddable_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddableEnhanced title: "embeddableEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddableEnhanced plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddableEnhanced'] --- import embeddableEnhancedObj from './embeddable_enhanced.devdocs.json'; diff --git a/api_docs/encrypted_saved_objects.mdx b/api_docs/encrypted_saved_objects.mdx index 71b8222b35a9e..4f5b0901138fc 100644 --- a/api_docs/encrypted_saved_objects.mdx +++ b/api_docs/encrypted_saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/encryptedSavedObjects title: "encryptedSavedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the encryptedSavedObjects plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'encryptedSavedObjects'] --- import encryptedSavedObjectsObj from './encrypted_saved_objects.devdocs.json'; diff --git a/api_docs/enterprise_search.mdx b/api_docs/enterprise_search.mdx index e5cb004fb95e4..666593267385b 100644 --- a/api_docs/enterprise_search.mdx +++ b/api_docs/enterprise_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/enterpriseSearch title: "enterpriseSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the enterpriseSearch plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'enterpriseSearch'] --- import enterpriseSearchObj from './enterprise_search.devdocs.json'; diff --git a/api_docs/es_ui_shared.mdx b/api_docs/es_ui_shared.mdx index 34ad6afa48b70..a623814d2d906 100644 --- a/api_docs/es_ui_shared.mdx +++ b/api_docs/es_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esUiShared title: "esUiShared" image: https://source.unsplash.com/400x175/?github description: API docs for the esUiShared plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] --- import esUiSharedObj from './es_ui_shared.devdocs.json'; diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index 194622fbc27a2..e48a46cfcb57a 100644 --- a/api_docs/event_annotation.mdx +++ b/api_docs/event_annotation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotation title: "eventAnnotation" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotation plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] --- import eventAnnotationObj from './event_annotation.devdocs.json'; diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index 0ddef098dd5b9..09c8d934dfea1 100644 --- a/api_docs/event_log.mdx +++ b/api_docs/event_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventLog title: "eventLog" image: https://source.unsplash.com/400x175/?github description: API docs for the eventLog plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] --- import eventLogObj from './event_log.devdocs.json'; diff --git a/api_docs/exploratory_view.mdx b/api_docs/exploratory_view.mdx index 20529d268a290..2fc551f75db51 100644 --- a/api_docs/exploratory_view.mdx +++ b/api_docs/exploratory_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/exploratoryView title: "exploratoryView" image: https://source.unsplash.com/400x175/?github description: API docs for the exploratoryView plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'exploratoryView'] --- import exploratoryViewObj from './exploratory_view.devdocs.json'; diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index b5dc06cd852ce..ffadc334f7246 100644 --- a/api_docs/expression_error.mdx +++ b/api_docs/expression_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionError title: "expressionError" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionError plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionError'] --- import expressionErrorObj from './expression_error.devdocs.json'; diff --git a/api_docs/expression_gauge.mdx b/api_docs/expression_gauge.mdx index 428938bb6e43b..099b5e151ad5b 100644 --- a/api_docs/expression_gauge.mdx +++ b/api_docs/expression_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionGauge title: "expressionGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionGauge plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionGauge'] --- import expressionGaugeObj from './expression_gauge.devdocs.json'; diff --git a/api_docs/expression_heatmap.mdx b/api_docs/expression_heatmap.mdx index 7d82284b88156..50567fb3c9bd4 100644 --- a/api_docs/expression_heatmap.mdx +++ b/api_docs/expression_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionHeatmap title: "expressionHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionHeatmap plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionHeatmap'] --- import expressionHeatmapObj from './expression_heatmap.devdocs.json'; diff --git a/api_docs/expression_image.mdx b/api_docs/expression_image.mdx index ab3f91ea6f27a..511647d6a7650 100644 --- a/api_docs/expression_image.mdx +++ b/api_docs/expression_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionImage title: "expressionImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionImage plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionImage'] --- import expressionImageObj from './expression_image.devdocs.json'; diff --git a/api_docs/expression_legacy_metric_vis.mdx b/api_docs/expression_legacy_metric_vis.mdx index 4fe62e71f5ac1..5a8071726a871 100644 --- a/api_docs/expression_legacy_metric_vis.mdx +++ b/api_docs/expression_legacy_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionLegacyMetricVis title: "expressionLegacyMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionLegacyMetricVis plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionLegacyMetricVis'] --- import expressionLegacyMetricVisObj from './expression_legacy_metric_vis.devdocs.json'; diff --git a/api_docs/expression_metric.mdx b/api_docs/expression_metric.mdx index 09341a4b671a8..84d8834f2e1a9 100644 --- a/api_docs/expression_metric.mdx +++ b/api_docs/expression_metric.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetric title: "expressionMetric" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetric plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetric'] --- import expressionMetricObj from './expression_metric.devdocs.json'; diff --git a/api_docs/expression_metric_vis.mdx b/api_docs/expression_metric_vis.mdx index 03f7b9f156c9d..bd596eef5f64b 100644 --- a/api_docs/expression_metric_vis.mdx +++ b/api_docs/expression_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetricVis title: "expressionMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetricVis plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetricVis'] --- import expressionMetricVisObj from './expression_metric_vis.devdocs.json'; diff --git a/api_docs/expression_partition_vis.mdx b/api_docs/expression_partition_vis.mdx index 5595380a5145f..012b3c8283002 100644 --- a/api_docs/expression_partition_vis.mdx +++ b/api_docs/expression_partition_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionPartitionVis title: "expressionPartitionVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionPartitionVis plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionPartitionVis'] --- import expressionPartitionVisObj from './expression_partition_vis.devdocs.json'; diff --git a/api_docs/expression_repeat_image.mdx b/api_docs/expression_repeat_image.mdx index 9ab0c0b0ef36c..5e5fa0092ce64 100644 --- a/api_docs/expression_repeat_image.mdx +++ b/api_docs/expression_repeat_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRepeatImage title: "expressionRepeatImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRepeatImage plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRepeatImage'] --- import expressionRepeatImageObj from './expression_repeat_image.devdocs.json'; diff --git a/api_docs/expression_reveal_image.mdx b/api_docs/expression_reveal_image.mdx index 99d0de37e515c..5a3567bbaa35b 100644 --- a/api_docs/expression_reveal_image.mdx +++ b/api_docs/expression_reveal_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRevealImage title: "expressionRevealImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRevealImage plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRevealImage'] --- import expressionRevealImageObj from './expression_reveal_image.devdocs.json'; diff --git a/api_docs/expression_shape.mdx b/api_docs/expression_shape.mdx index 719e8466bb47b..d75699a0bc67b 100644 --- a/api_docs/expression_shape.mdx +++ b/api_docs/expression_shape.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionShape title: "expressionShape" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionShape plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionShape'] --- import expressionShapeObj from './expression_shape.devdocs.json'; diff --git a/api_docs/expression_tagcloud.mdx b/api_docs/expression_tagcloud.mdx index 4bf1d7c003789..3a2af466166ea 100644 --- a/api_docs/expression_tagcloud.mdx +++ b/api_docs/expression_tagcloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionTagcloud title: "expressionTagcloud" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionTagcloud plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionTagcloud'] --- import expressionTagcloudObj from './expression_tagcloud.devdocs.json'; diff --git a/api_docs/expression_x_y.mdx b/api_docs/expression_x_y.mdx index e0242a8e36c00..0b67bdbb64966 100644 --- a/api_docs/expression_x_y.mdx +++ b/api_docs/expression_x_y.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionXY title: "expressionXY" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionXY plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionXY'] --- import expressionXYObj from './expression_x_y.devdocs.json'; diff --git a/api_docs/expressions.mdx b/api_docs/expressions.mdx index 947074207702c..06f029f56890d 100644 --- a/api_docs/expressions.mdx +++ b/api_docs/expressions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressions title: "expressions" image: https://source.unsplash.com/400x175/?github description: API docs for the expressions plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressions'] --- import expressionsObj from './expressions.devdocs.json'; diff --git a/api_docs/features.mdx b/api_docs/features.mdx index 3c59782f81b39..4cfb788a38eaa 100644 --- a/api_docs/features.mdx +++ b/api_docs/features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/features title: "features" image: https://source.unsplash.com/400x175/?github description: API docs for the features plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'features'] --- import featuresObj from './features.devdocs.json'; diff --git a/api_docs/field_formats.mdx b/api_docs/field_formats.mdx index d700e1b5d69aa..a236ab0be0474 100644 --- a/api_docs/field_formats.mdx +++ b/api_docs/field_formats.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fieldFormats title: "fieldFormats" image: https://source.unsplash.com/400x175/?github description: API docs for the fieldFormats plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldFormats'] --- import fieldFormatsObj from './field_formats.devdocs.json'; diff --git a/api_docs/file_upload.mdx b/api_docs/file_upload.mdx index a61a9bd41640d..23c3684b63bef 100644 --- a/api_docs/file_upload.mdx +++ b/api_docs/file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fileUpload title: "fileUpload" image: https://source.unsplash.com/400x175/?github description: API docs for the fileUpload plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fileUpload'] --- import fileUploadObj from './file_upload.devdocs.json'; diff --git a/api_docs/files.mdx b/api_docs/files.mdx index 5585cae642f0c..9c5b048df424c 100644 --- a/api_docs/files.mdx +++ b/api_docs/files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/files title: "files" image: https://source.unsplash.com/400x175/?github description: API docs for the files plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'files'] --- import filesObj from './files.devdocs.json'; diff --git a/api_docs/files_management.mdx b/api_docs/files_management.mdx index 43b94e5b0c524..74a3b30e990d2 100644 --- a/api_docs/files_management.mdx +++ b/api_docs/files_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/filesManagement title: "filesManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the filesManagement plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'filesManagement'] --- import filesManagementObj from './files_management.devdocs.json'; diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index b895759be2757..d4770a5393235 100644 --- a/api_docs/fleet.mdx +++ b/api_docs/fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fleet title: "fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the fleet plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fleet'] --- import fleetObj from './fleet.devdocs.json'; diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index 583e61b08e22c..c8dc6be2ffc7a 100644 --- a/api_docs/global_search.mdx +++ b/api_docs/global_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/globalSearch title: "globalSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the globalSearch plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'globalSearch'] --- import globalSearchObj from './global_search.devdocs.json'; diff --git a/api_docs/guided_onboarding.mdx b/api_docs/guided_onboarding.mdx index 7e6223846ca2e..9cf887d5ed9c9 100644 --- a/api_docs/guided_onboarding.mdx +++ b/api_docs/guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/guidedOnboarding title: "guidedOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the guidedOnboarding plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'guidedOnboarding'] --- import guidedOnboardingObj from './guided_onboarding.devdocs.json'; diff --git a/api_docs/home.mdx b/api_docs/home.mdx index 57c9d811644ef..2384ea3a83b81 100644 --- a/api_docs/home.mdx +++ b/api_docs/home.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/home title: "home" image: https://source.unsplash.com/400x175/?github description: API docs for the home plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'home'] --- import homeObj from './home.devdocs.json'; diff --git a/api_docs/image_embeddable.mdx b/api_docs/image_embeddable.mdx index 348456e53d1d6..3a4b6ea650395 100644 --- a/api_docs/image_embeddable.mdx +++ b/api_docs/image_embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/imageEmbeddable title: "imageEmbeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the imageEmbeddable plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'imageEmbeddable'] --- import imageEmbeddableObj from './image_embeddable.devdocs.json'; diff --git a/api_docs/index_lifecycle_management.mdx b/api_docs/index_lifecycle_management.mdx index 1d371306c95b1..f35561697892e 100644 --- a/api_docs/index_lifecycle_management.mdx +++ b/api_docs/index_lifecycle_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexLifecycleManagement title: "indexLifecycleManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexLifecycleManagement plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexLifecycleManagement'] --- import indexLifecycleManagementObj from './index_lifecycle_management.devdocs.json'; diff --git a/api_docs/index_management.devdocs.json b/api_docs/index_management.devdocs.json index 3da3a9bee799e..6d3b01d5245cf 100644 --- a/api_docs/index_management.devdocs.json +++ b/api_docs/index_management.devdocs.json @@ -201,6 +201,48 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "indexManagement", + "id": "def-public.Index.isRollupIndex", + "type": "CompoundType", + "tags": [], + "label": "isRollupIndex", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/index_management/common/types/indices.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "indexManagement", + "id": "def-public.Index.ilm", + "type": "Object", + "tags": [], + "label": "ilm", + "description": [], + "signature": [ + "{ index: string; managed: boolean; } | undefined" + ], + "path": "x-pack/plugins/index_management/common/types/indices.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "indexManagement", + "id": "def-public.Index.isFollowerIndex", + "type": "CompoundType", + "tags": [], + "label": "isFollowerIndex", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/index_management/common/types/indices.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "indexManagement", "id": "def-public.Index.health", @@ -529,6 +571,48 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "indexManagement", + "id": "def-server.Index.isRollupIndex", + "type": "CompoundType", + "tags": [], + "label": "isRollupIndex", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/index_management/common/types/indices.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "indexManagement", + "id": "def-server.Index.ilm", + "type": "Object", + "tags": [], + "label": "ilm", + "description": [], + "signature": [ + "{ index: string; managed: boolean; } | undefined" + ], + "path": "x-pack/plugins/index_management/common/types/indices.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "indexManagement", + "id": "def-server.Index.isFollowerIndex", + "type": "CompoundType", + "tags": [], + "label": "isFollowerIndex", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/index_management/common/types/indices.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "indexManagement", "id": "def-server.Index.health", @@ -1728,6 +1812,48 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "indexManagement", + "id": "def-common.Index.isRollupIndex", + "type": "CompoundType", + "tags": [], + "label": "isRollupIndex", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/index_management/common/types/indices.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "indexManagement", + "id": "def-common.Index.ilm", + "type": "Object", + "tags": [], + "label": "ilm", + "description": [], + "signature": [ + "{ index: string; managed: boolean; } | undefined" + ], + "path": "x-pack/plugins/index_management/common/types/indices.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "indexManagement", + "id": "def-common.Index.isFollowerIndex", + "type": "CompoundType", + "tags": [], + "label": "isFollowerIndex", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/index_management/common/types/indices.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "indexManagement", "id": "def-common.Index.health", diff --git a/api_docs/index_management.mdx b/api_docs/index_management.mdx index 0656f447f754b..61ea64ac08109 100644 --- a/api_docs/index_management.mdx +++ b/api_docs/index_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexManagement title: "indexManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexManagement plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement'] --- import indexManagementObj from './index_management.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/platform-deployment-management](https://github.com/orgs/elasti | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 182 | 0 | 177 | 4 | +| 191 | 0 | 186 | 4 | ## Client diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx index 89c1ad1fe9a1a..e9596cddc338c 100644 --- a/api_docs/infra.mdx +++ b/api_docs/infra.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/infra title: "infra" image: https://source.unsplash.com/400x175/?github description: API docs for the infra plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] --- import infraObj from './infra.devdocs.json'; diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index af92b068faab9..e0a6543f30b6f 100644 --- a/api_docs/inspector.mdx +++ b/api_docs/inspector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inspector title: "inspector" image: https://source.unsplash.com/400x175/?github description: API docs for the inspector plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inspector'] --- import inspectorObj from './inspector.devdocs.json'; diff --git a/api_docs/interactive_setup.mdx b/api_docs/interactive_setup.mdx index 08df446855ac1..72cc862872291 100644 --- a/api_docs/interactive_setup.mdx +++ b/api_docs/interactive_setup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/interactiveSetup title: "interactiveSetup" image: https://source.unsplash.com/400x175/?github description: API docs for the interactiveSetup plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'interactiveSetup'] --- import interactiveSetupObj from './interactive_setup.devdocs.json'; diff --git a/api_docs/kbn_ace.mdx b/api_docs/kbn_ace.mdx index 82c4099c75850..3c5907d8de45c 100644 --- a/api_docs/kbn_ace.mdx +++ b/api_docs/kbn_ace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ace title: "@kbn/ace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ace plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ace'] --- import kbnAceObj from './kbn_ace.devdocs.json'; diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index 3275641350d15..53fecfd5a7ddd 100644 --- a/api_docs/kbn_aiops_components.mdx +++ b/api_docs/kbn_aiops_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-components title: "@kbn/aiops-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-components plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_utils.mdx b/api_docs/kbn_aiops_utils.mdx index ff79bf58afb09..c8974ddedaf08 100644 --- a/api_docs/kbn_aiops_utils.mdx +++ b/api_docs/kbn_aiops_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-utils title: "@kbn/aiops-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-utils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-utils'] --- import kbnAiopsUtilsObj from './kbn_aiops_utils.devdocs.json'; diff --git a/api_docs/kbn_alerting_api_integration_helpers.mdx b/api_docs/kbn_alerting_api_integration_helpers.mdx index df262c8e51c64..d37c1916e8378 100644 --- a/api_docs/kbn_alerting_api_integration_helpers.mdx +++ b/api_docs/kbn_alerting_api_integration_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-api-integration-helpers title: "@kbn/alerting-api-integration-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-api-integration-helpers plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-api-integration-helpers'] --- import kbnAlertingApiIntegrationHelpersObj from './kbn_alerting_api_integration_helpers.devdocs.json'; diff --git a/api_docs/kbn_alerting_state_types.mdx b/api_docs/kbn_alerting_state_types.mdx index 925e234abb67c..364e1880c198f 100644 --- a/api_docs/kbn_alerting_state_types.mdx +++ b/api_docs/kbn_alerting_state_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-state-types title: "@kbn/alerting-state-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-state-types plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-state-types'] --- import kbnAlertingStateTypesObj from './kbn_alerting_state_types.devdocs.json'; diff --git a/api_docs/kbn_alerts_as_data_utils.mdx b/api_docs/kbn_alerts_as_data_utils.mdx index dfe61d9785b2c..d631150c0a90f 100644 --- a/api_docs/kbn_alerts_as_data_utils.mdx +++ b/api_docs/kbn_alerts_as_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-as-data-utils title: "@kbn/alerts-as-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-as-data-utils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-as-data-utils'] --- import kbnAlertsAsDataUtilsObj from './kbn_alerts_as_data_utils.devdocs.json'; diff --git a/api_docs/kbn_alerts_ui_shared.mdx b/api_docs/kbn_alerts_ui_shared.mdx index 21bdc8a1031cd..f6099e3a94a3a 100644 --- a/api_docs/kbn_alerts_ui_shared.mdx +++ b/api_docs/kbn_alerts_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-ui-shared title: "@kbn/alerts-ui-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-ui-shared plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-ui-shared'] --- import kbnAlertsUiSharedObj from './kbn_alerts_ui_shared.devdocs.json'; diff --git a/api_docs/kbn_analytics.mdx b/api_docs/kbn_analytics.mdx index 76110b5e4764f..ecef44beb6612 100644 --- a/api_docs/kbn_analytics.mdx +++ b/api_docs/kbn_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics title: "@kbn/analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index 1bddd8dd1dc50..fa2f5ee2548fd 100644 --- a/api_docs/kbn_analytics_client.mdx +++ b/api_docs/kbn_analytics_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-client title: "@kbn/analytics-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-client plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-client'] --- import kbnAnalyticsClientObj from './kbn_analytics_client.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx index 9277893dc5918..992023681a5b7 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-browser title: "@kbn/analytics-shippers-elastic-v3-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-browser plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-browser'] --- import kbnAnalyticsShippersElasticV3BrowserObj from './kbn_analytics_shippers_elastic_v3_browser.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx index c67b4e148997d..d469e52483593 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-common title: "@kbn/analytics-shippers-elastic-v3-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-common plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-common'] --- import kbnAnalyticsShippersElasticV3CommonObj from './kbn_analytics_shippers_elastic_v3_common.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx index 10b68beca9e5e..fe0c82020289a 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-server title: "@kbn/analytics-shippers-elastic-v3-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-server plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-server'] --- import kbnAnalyticsShippersElasticV3ServerObj from './kbn_analytics_shippers_elastic_v3_server.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_fullstory.mdx b/api_docs/kbn_analytics_shippers_fullstory.mdx index 94b226a5da6f4..29577b4aa6140 100644 --- a/api_docs/kbn_analytics_shippers_fullstory.mdx +++ b/api_docs/kbn_analytics_shippers_fullstory.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-fullstory title: "@kbn/analytics-shippers-fullstory" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-fullstory plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] --- import kbnAnalyticsShippersFullstoryObj from './kbn_analytics_shippers_fullstory.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_gainsight.mdx b/api_docs/kbn_analytics_shippers_gainsight.mdx index c44d454379a9e..c495acdb88da5 100644 --- a/api_docs/kbn_analytics_shippers_gainsight.mdx +++ b/api_docs/kbn_analytics_shippers_gainsight.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-gainsight title: "@kbn/analytics-shippers-gainsight" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-gainsight plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-gainsight'] --- import kbnAnalyticsShippersGainsightObj from './kbn_analytics_shippers_gainsight.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index 6bf641fba5afc..ddb75b94a1d03 100644 --- a/api_docs/kbn_apm_config_loader.mdx +++ b/api_docs/kbn_apm_config_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-config-loader title: "@kbn/apm-config-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-config-loader plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-config-loader'] --- import kbnApmConfigLoaderObj from './kbn_apm_config_loader.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index 97420bab75163..feaecf2c009a5 100644 --- a/api_docs/kbn_apm_synthtrace.mdx +++ b/api_docs/kbn_apm_synthtrace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace title: "@kbn/apm-synthtrace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace'] --- import kbnApmSynthtraceObj from './kbn_apm_synthtrace.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace_client.mdx b/api_docs/kbn_apm_synthtrace_client.mdx index af44e635f73d8..0b1c54700b357 100644 --- a/api_docs/kbn_apm_synthtrace_client.mdx +++ b/api_docs/kbn_apm_synthtrace_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace-client title: "@kbn/apm-synthtrace-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace-client plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace-client'] --- import kbnApmSynthtraceClientObj from './kbn_apm_synthtrace_client.devdocs.json'; diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index 0318e42262b74..30662ff02b580 100644 --- a/api_docs/kbn_apm_utils.mdx +++ b/api_docs/kbn_apm_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-utils title: "@kbn/apm-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-utils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-utils'] --- import kbnApmUtilsObj from './kbn_apm_utils.devdocs.json'; diff --git a/api_docs/kbn_axe_config.mdx b/api_docs/kbn_axe_config.mdx index 1b6654ef2255c..c8d93573025a8 100644 --- a/api_docs/kbn_axe_config.mdx +++ b/api_docs/kbn_axe_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-axe-config title: "@kbn/axe-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/axe-config plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_cases_components.mdx b/api_docs/kbn_cases_components.mdx index 81344b3241e95..03fe88c6c2db0 100644 --- a/api_docs/kbn_cases_components.mdx +++ b/api_docs/kbn_cases_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cases-components title: "@kbn/cases-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cases-components plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cases-components'] --- import kbnCasesComponentsObj from './kbn_cases_components.devdocs.json'; diff --git a/api_docs/kbn_cell_actions.mdx b/api_docs/kbn_cell_actions.mdx index 18628107149c5..a1c1fd44d27b8 100644 --- a/api_docs/kbn_cell_actions.mdx +++ b/api_docs/kbn_cell_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cell-actions title: "@kbn/cell-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cell-actions plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cell-actions'] --- import kbnCellActionsObj from './kbn_cell_actions.devdocs.json'; diff --git a/api_docs/kbn_chart_expressions_common.mdx b/api_docs/kbn_chart_expressions_common.mdx index 83452450ef094..717ee4f9d9bda 100644 --- a/api_docs/kbn_chart_expressions_common.mdx +++ b/api_docs/kbn_chart_expressions_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-expressions-common title: "@kbn/chart-expressions-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-expressions-common plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/chart-expressions-common'] --- import kbnChartExpressionsCommonObj from './kbn_chart_expressions_common.devdocs.json'; diff --git a/api_docs/kbn_chart_icons.mdx b/api_docs/kbn_chart_icons.mdx index f664622736c9b..ec7b964886ed3 100644 --- a/api_docs/kbn_chart_icons.mdx +++ b/api_docs/kbn_chart_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-icons title: "@kbn/chart-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-icons plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/chart-icons'] --- import kbnChartIconsObj from './kbn_chart_icons.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_core.mdx b/api_docs/kbn_ci_stats_core.mdx index 4d9b4aeb058c3..1fa4cea3f980d 100644 --- a/api_docs/kbn_ci_stats_core.mdx +++ b/api_docs/kbn_ci_stats_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-core title: "@kbn/ci-stats-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-core plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-core'] --- import kbnCiStatsCoreObj from './kbn_ci_stats_core.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_performance_metrics.mdx b/api_docs/kbn_ci_stats_performance_metrics.mdx index bfc6769604c0e..bf0a79fc42361 100644 --- a/api_docs/kbn_ci_stats_performance_metrics.mdx +++ b/api_docs/kbn_ci_stats_performance_metrics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-performance-metrics title: "@kbn/ci-stats-performance-metrics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-performance-metrics plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-performance-metrics'] --- import kbnCiStatsPerformanceMetricsObj from './kbn_ci_stats_performance_metrics.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_reporter.mdx b/api_docs/kbn_ci_stats_reporter.mdx index d0dcb11b93ed6..b831fb8a168bf 100644 --- a/api_docs/kbn_ci_stats_reporter.mdx +++ b/api_docs/kbn_ci_stats_reporter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-reporter title: "@kbn/ci-stats-reporter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-reporter plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-reporter'] --- import kbnCiStatsReporterObj from './kbn_ci_stats_reporter.devdocs.json'; diff --git a/api_docs/kbn_cli_dev_mode.mdx b/api_docs/kbn_cli_dev_mode.mdx index 9008611a23d50..5cae7304d6845 100644 --- a/api_docs/kbn_cli_dev_mode.mdx +++ b/api_docs/kbn_cli_dev_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cli-dev-mode title: "@kbn/cli-dev-mode" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cli-dev-mode plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] --- import kbnCliDevModeObj from './kbn_cli_dev_mode.devdocs.json'; diff --git a/api_docs/kbn_code_editor.mdx b/api_docs/kbn_code_editor.mdx index c0791e49ce425..7b3670e6fde6c 100644 --- a/api_docs/kbn_code_editor.mdx +++ b/api_docs/kbn_code_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor title: "@kbn/code-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor'] --- import kbnCodeEditorObj from './kbn_code_editor.devdocs.json'; diff --git a/api_docs/kbn_code_editor_mocks.mdx b/api_docs/kbn_code_editor_mocks.mdx index f84fc43805710..883e7b41fdf2b 100644 --- a/api_docs/kbn_code_editor_mocks.mdx +++ b/api_docs/kbn_code_editor_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor-mocks title: "@kbn/code-editor-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor-mocks'] --- import kbnCodeEditorMocksObj from './kbn_code_editor_mocks.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index b67b695e134ac..e145f92192da3 100644 --- a/api_docs/kbn_coloring.mdx +++ b/api_docs/kbn_coloring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-coloring title: "@kbn/coloring" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/coloring plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/coloring'] --- import kbnColoringObj from './kbn_coloring.devdocs.json'; diff --git a/api_docs/kbn_config.mdx b/api_docs/kbn_config.mdx index 5dcdd592f9b23..49ccc8f40a321 100644 --- a/api_docs/kbn_config.mdx +++ b/api_docs/kbn_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config title: "@kbn/config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config'] --- import kbnConfigObj from './kbn_config.devdocs.json'; diff --git a/api_docs/kbn_config_mocks.mdx b/api_docs/kbn_config_mocks.mdx index c9c53e99ab13d..857ac58bf99a5 100644 --- a/api_docs/kbn_config_mocks.mdx +++ b/api_docs/kbn_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-mocks title: "@kbn/config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-mocks'] --- import kbnConfigMocksObj from './kbn_config_mocks.devdocs.json'; diff --git a/api_docs/kbn_config_schema.mdx b/api_docs/kbn_config_schema.mdx index 69e3d3abb49a3..0a55f16979007 100644 --- a/api_docs/kbn_config_schema.mdx +++ b/api_docs/kbn_config_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-schema title: "@kbn/config-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-schema plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-schema'] --- import kbnConfigSchemaObj from './kbn_config_schema.devdocs.json'; diff --git a/api_docs/kbn_content_management_content_editor.mdx b/api_docs/kbn_content_management_content_editor.mdx index d03291aaec6a0..6656664fcefff 100644 --- a/api_docs/kbn_content_management_content_editor.mdx +++ b/api_docs/kbn_content_management_content_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-content-editor title: "@kbn/content-management-content-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-content-editor plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-content-editor'] --- import kbnContentManagementContentEditorObj from './kbn_content_management_content_editor.devdocs.json'; diff --git a/api_docs/kbn_content_management_tabbed_table_list_view.mdx b/api_docs/kbn_content_management_tabbed_table_list_view.mdx index fe7b3d1f34b88..ec829c7248be6 100644 --- a/api_docs/kbn_content_management_tabbed_table_list_view.mdx +++ b/api_docs/kbn_content_management_tabbed_table_list_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-tabbed-table-list-view title: "@kbn/content-management-tabbed-table-list-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-tabbed-table-list-view plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-tabbed-table-list-view'] --- import kbnContentManagementTabbedTableListViewObj from './kbn_content_management_tabbed_table_list_view.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view.mdx b/api_docs/kbn_content_management_table_list_view.mdx index 75f585c7d1a9c..da0372ad77175 100644 --- a/api_docs/kbn_content_management_table_list_view.mdx +++ b/api_docs/kbn_content_management_table_list_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view title: "@kbn/content-management-table-list-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view'] --- import kbnContentManagementTableListViewObj from './kbn_content_management_table_list_view.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view_table.mdx b/api_docs/kbn_content_management_table_list_view_table.mdx index 0e8dd462cf74f..f4f02e0699ed3 100644 --- a/api_docs/kbn_content_management_table_list_view_table.mdx +++ b/api_docs/kbn_content_management_table_list_view_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view-table title: "@kbn/content-management-table-list-view-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view-table plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view-table'] --- import kbnContentManagementTableListViewTableObj from './kbn_content_management_table_list_view_table.devdocs.json'; diff --git a/api_docs/kbn_content_management_utils.mdx b/api_docs/kbn_content_management_utils.mdx index df3d2815d0275..60e161f3b7873 100644 --- a/api_docs/kbn_content_management_utils.mdx +++ b/api_docs/kbn_content_management_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-utils title: "@kbn/content-management-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-utils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-utils'] --- import kbnContentManagementUtilsObj from './kbn_content_management_utils.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index d05bb64cead09..0f19d1757debe 100644 --- a/api_docs/kbn_core_analytics_browser.mdx +++ b/api_docs/kbn_core_analytics_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser title: "@kbn/core-analytics-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser'] --- import kbnCoreAnalyticsBrowserObj from './kbn_core_analytics_browser.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser_internal.mdx b/api_docs/kbn_core_analytics_browser_internal.mdx index 8d9d21e7a8059..65ae7d9d5c525 100644 --- a/api_docs/kbn_core_analytics_browser_internal.mdx +++ b/api_docs/kbn_core_analytics_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-internal title: "@kbn/core-analytics-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-internal'] --- import kbnCoreAnalyticsBrowserInternalObj from './kbn_core_analytics_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser_mocks.mdx b/api_docs/kbn_core_analytics_browser_mocks.mdx index 6f42bb3cee9cf..fd06213dc58c4 100644 --- a/api_docs/kbn_core_analytics_browser_mocks.mdx +++ b/api_docs/kbn_core_analytics_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-mocks title: "@kbn/core-analytics-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-mocks'] --- import kbnCoreAnalyticsBrowserMocksObj from './kbn_core_analytics_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server.mdx b/api_docs/kbn_core_analytics_server.mdx index 5a532edd942ec..273d35d08fe5b 100644 --- a/api_docs/kbn_core_analytics_server.mdx +++ b/api_docs/kbn_core_analytics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server title: "@kbn/core-analytics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server'] --- import kbnCoreAnalyticsServerObj from './kbn_core_analytics_server.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server_internal.mdx b/api_docs/kbn_core_analytics_server_internal.mdx index b7f1279e37fb4..50dfec92d8cc8 100644 --- a/api_docs/kbn_core_analytics_server_internal.mdx +++ b/api_docs/kbn_core_analytics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-internal title: "@kbn/core-analytics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-internal'] --- import kbnCoreAnalyticsServerInternalObj from './kbn_core_analytics_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server_mocks.mdx b/api_docs/kbn_core_analytics_server_mocks.mdx index 305d8000439ba..2796d4b9348a5 100644 --- a/api_docs/kbn_core_analytics_server_mocks.mdx +++ b/api_docs/kbn_core_analytics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-mocks title: "@kbn/core-analytics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-mocks'] --- import kbnCoreAnalyticsServerMocksObj from './kbn_core_analytics_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser.mdx b/api_docs/kbn_core_application_browser.mdx index 3ff039e7893ce..5343660f95030 100644 --- a/api_docs/kbn_core_application_browser.mdx +++ b/api_docs/kbn_core_application_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser title: "@kbn/core-application-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser'] --- import kbnCoreApplicationBrowserObj from './kbn_core_application_browser.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser_internal.mdx b/api_docs/kbn_core_application_browser_internal.mdx index 17ecf4be390b7..514788b96f802 100644 --- a/api_docs/kbn_core_application_browser_internal.mdx +++ b/api_docs/kbn_core_application_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-internal title: "@kbn/core-application-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser-internal'] --- import kbnCoreApplicationBrowserInternalObj from './kbn_core_application_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser_mocks.mdx b/api_docs/kbn_core_application_browser_mocks.mdx index 8cde1e23fe26e..bb65de3706a63 100644 --- a/api_docs/kbn_core_application_browser_mocks.mdx +++ b/api_docs/kbn_core_application_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-mocks title: "@kbn/core-application-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser-mocks'] --- import kbnCoreApplicationBrowserMocksObj from './kbn_core_application_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_application_common.mdx b/api_docs/kbn_core_application_common.mdx index 9ef4e2e08ac09..771ddee3d32b2 100644 --- a/api_docs/kbn_core_application_common.mdx +++ b/api_docs/kbn_core_application_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-common title: "@kbn/core-application-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-common plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-common'] --- import kbnCoreApplicationCommonObj from './kbn_core_application_common.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_internal.mdx b/api_docs/kbn_core_apps_browser_internal.mdx index 66f382df62eb1..f41a779f11ecf 100644 --- a/api_docs/kbn_core_apps_browser_internal.mdx +++ b/api_docs/kbn_core_apps_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-internal title: "@kbn/core-apps-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-internal'] --- import kbnCoreAppsBrowserInternalObj from './kbn_core_apps_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_mocks.mdx b/api_docs/kbn_core_apps_browser_mocks.mdx index ddf50e2177573..877911f915135 100644 --- a/api_docs/kbn_core_apps_browser_mocks.mdx +++ b/api_docs/kbn_core_apps_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-mocks title: "@kbn/core-apps-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-mocks'] --- import kbnCoreAppsBrowserMocksObj from './kbn_core_apps_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_apps_server_internal.mdx b/api_docs/kbn_core_apps_server_internal.mdx index d8245b2d4e513..02b9fba56b31b 100644 --- a/api_docs/kbn_core_apps_server_internal.mdx +++ b/api_docs/kbn_core_apps_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-server-internal title: "@kbn/core-apps-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-server-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-server-internal'] --- import kbnCoreAppsServerInternalObj from './kbn_core_apps_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_base_browser_mocks.mdx b/api_docs/kbn_core_base_browser_mocks.mdx index 61a7c4d3e8db4..f0146e929be94 100644 --- a/api_docs/kbn_core_base_browser_mocks.mdx +++ b/api_docs/kbn_core_base_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-browser-mocks title: "@kbn/core-base-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-browser-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-browser-mocks'] --- import kbnCoreBaseBrowserMocksObj from './kbn_core_base_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_base_common.mdx b/api_docs/kbn_core_base_common.mdx index 9a725c0f241a4..df575fdd3c903 100644 --- a/api_docs/kbn_core_base_common.mdx +++ b/api_docs/kbn_core_base_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-common title: "@kbn/core-base-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-common plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-common'] --- import kbnCoreBaseCommonObj from './kbn_core_base_common.devdocs.json'; diff --git a/api_docs/kbn_core_base_server_internal.mdx b/api_docs/kbn_core_base_server_internal.mdx index 1d4e79a8d64e1..92f94a60dd217 100644 --- a/api_docs/kbn_core_base_server_internal.mdx +++ b/api_docs/kbn_core_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-internal title: "@kbn/core-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-internal'] --- import kbnCoreBaseServerInternalObj from './kbn_core_base_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_base_server_mocks.mdx b/api_docs/kbn_core_base_server_mocks.mdx index 0d3ae2a2735d1..86b23da543b7b 100644 --- a/api_docs/kbn_core_base_server_mocks.mdx +++ b/api_docs/kbn_core_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-mocks title: "@kbn/core-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-mocks'] --- import kbnCoreBaseServerMocksObj from './kbn_core_base_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_browser_mocks.mdx b/api_docs/kbn_core_capabilities_browser_mocks.mdx index 161063403ce46..e8bfa3479d1dd 100644 --- a/api_docs/kbn_core_capabilities_browser_mocks.mdx +++ b/api_docs/kbn_core_capabilities_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-browser-mocks title: "@kbn/core-capabilities-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-browser-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-browser-mocks'] --- import kbnCoreCapabilitiesBrowserMocksObj from './kbn_core_capabilities_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_common.mdx b/api_docs/kbn_core_capabilities_common.mdx index e8a1e09f1e2b3..51ae5d342d263 100644 --- a/api_docs/kbn_core_capabilities_common.mdx +++ b/api_docs/kbn_core_capabilities_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-common title: "@kbn/core-capabilities-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-common plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-common'] --- import kbnCoreCapabilitiesCommonObj from './kbn_core_capabilities_common.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_server.mdx b/api_docs/kbn_core_capabilities_server.mdx index 3120bbc163d5d..2b977caea7b35 100644 --- a/api_docs/kbn_core_capabilities_server.mdx +++ b/api_docs/kbn_core_capabilities_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server title: "@kbn/core-capabilities-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server'] --- import kbnCoreCapabilitiesServerObj from './kbn_core_capabilities_server.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_server_mocks.mdx b/api_docs/kbn_core_capabilities_server_mocks.mdx index a79379b9a8f5a..ea7d5217cbabc 100644 --- a/api_docs/kbn_core_capabilities_server_mocks.mdx +++ b/api_docs/kbn_core_capabilities_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server-mocks title: "@kbn/core-capabilities-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server-mocks'] --- import kbnCoreCapabilitiesServerMocksObj from './kbn_core_capabilities_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser.mdx b/api_docs/kbn_core_chrome_browser.mdx index 590c8a853c9ab..f450451982d27 100644 --- a/api_docs/kbn_core_chrome_browser.mdx +++ b/api_docs/kbn_core_chrome_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser title: "@kbn/core-chrome-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser'] --- import kbnCoreChromeBrowserObj from './kbn_core_chrome_browser.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser_mocks.mdx b/api_docs/kbn_core_chrome_browser_mocks.mdx index bd49f3041871e..4c0e87652249f 100644 --- a/api_docs/kbn_core_chrome_browser_mocks.mdx +++ b/api_docs/kbn_core_chrome_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser-mocks title: "@kbn/core-chrome-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser-mocks'] --- import kbnCoreChromeBrowserMocksObj from './kbn_core_chrome_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_config_server_internal.mdx b/api_docs/kbn_core_config_server_internal.mdx index 36bdb034ca718..08372aed1a78e 100644 --- a/api_docs/kbn_core_config_server_internal.mdx +++ b/api_docs/kbn_core_config_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-config-server-internal title: "@kbn/core-config-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-config-server-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-config-server-internal'] --- import kbnCoreConfigServerInternalObj from './kbn_core_config_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser.mdx b/api_docs/kbn_core_custom_branding_browser.mdx index 8f5315346eeca..58a201eecff11 100644 --- a/api_docs/kbn_core_custom_branding_browser.mdx +++ b/api_docs/kbn_core_custom_branding_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser title: "@kbn/core-custom-branding-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser'] --- import kbnCoreCustomBrandingBrowserObj from './kbn_core_custom_branding_browser.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser_internal.mdx b/api_docs/kbn_core_custom_branding_browser_internal.mdx index d593b3b438138..fbbfd8d90c3b1 100644 --- a/api_docs/kbn_core_custom_branding_browser_internal.mdx +++ b/api_docs/kbn_core_custom_branding_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-internal title: "@kbn/core-custom-branding-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser-internal'] --- import kbnCoreCustomBrandingBrowserInternalObj from './kbn_core_custom_branding_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser_mocks.mdx b/api_docs/kbn_core_custom_branding_browser_mocks.mdx index 3e840fcf7af07..563028256c470 100644 --- a/api_docs/kbn_core_custom_branding_browser_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-mocks title: "@kbn/core-custom-branding-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser-mocks'] --- import kbnCoreCustomBrandingBrowserMocksObj from './kbn_core_custom_branding_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_common.mdx b/api_docs/kbn_core_custom_branding_common.mdx index 8065eceea0d5d..6f4bd9a5f598e 100644 --- a/api_docs/kbn_core_custom_branding_common.mdx +++ b/api_docs/kbn_core_custom_branding_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-common title: "@kbn/core-custom-branding-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-common plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-common'] --- import kbnCoreCustomBrandingCommonObj from './kbn_core_custom_branding_common.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server.mdx b/api_docs/kbn_core_custom_branding_server.mdx index 81e6809acd3d6..c8f70dd6eaf64 100644 --- a/api_docs/kbn_core_custom_branding_server.mdx +++ b/api_docs/kbn_core_custom_branding_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server title: "@kbn/core-custom-branding-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server'] --- import kbnCoreCustomBrandingServerObj from './kbn_core_custom_branding_server.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server_internal.mdx b/api_docs/kbn_core_custom_branding_server_internal.mdx index 45fe73cefea9e..058a0ff90fb30 100644 --- a/api_docs/kbn_core_custom_branding_server_internal.mdx +++ b/api_docs/kbn_core_custom_branding_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-internal title: "@kbn/core-custom-branding-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server-internal'] --- import kbnCoreCustomBrandingServerInternalObj from './kbn_core_custom_branding_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server_mocks.mdx b/api_docs/kbn_core_custom_branding_server_mocks.mdx index 1c62c7ba3bd7f..9dea0bbb5180e 100644 --- a/api_docs/kbn_core_custom_branding_server_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-mocks title: "@kbn/core-custom-branding-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server-mocks'] --- import kbnCoreCustomBrandingServerMocksObj from './kbn_core_custom_branding_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser.mdx b/api_docs/kbn_core_deprecations_browser.mdx index d79d8a4bd01b0..8bef97501ba14 100644 --- a/api_docs/kbn_core_deprecations_browser.mdx +++ b/api_docs/kbn_core_deprecations_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser title: "@kbn/core-deprecations-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser'] --- import kbnCoreDeprecationsBrowserObj from './kbn_core_deprecations_browser.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser_internal.mdx b/api_docs/kbn_core_deprecations_browser_internal.mdx index 587c030c177e3..86c0fd24d3ef5 100644 --- a/api_docs/kbn_core_deprecations_browser_internal.mdx +++ b/api_docs/kbn_core_deprecations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-internal title: "@kbn/core-deprecations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-internal'] --- import kbnCoreDeprecationsBrowserInternalObj from './kbn_core_deprecations_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser_mocks.mdx b/api_docs/kbn_core_deprecations_browser_mocks.mdx index 66fa8529c1a15..6d63989ec8b28 100644 --- a/api_docs/kbn_core_deprecations_browser_mocks.mdx +++ b/api_docs/kbn_core_deprecations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-mocks title: "@kbn/core-deprecations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-mocks'] --- import kbnCoreDeprecationsBrowserMocksObj from './kbn_core_deprecations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_common.mdx b/api_docs/kbn_core_deprecations_common.mdx index ffbfaa9720b0f..c860d1ad9e33a 100644 --- a/api_docs/kbn_core_deprecations_common.mdx +++ b/api_docs/kbn_core_deprecations_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-common title: "@kbn/core-deprecations-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-common plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-common'] --- import kbnCoreDeprecationsCommonObj from './kbn_core_deprecations_common.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server.mdx b/api_docs/kbn_core_deprecations_server.mdx index d794087e2740c..985ade7f9df50 100644 --- a/api_docs/kbn_core_deprecations_server.mdx +++ b/api_docs/kbn_core_deprecations_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server title: "@kbn/core-deprecations-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server'] --- import kbnCoreDeprecationsServerObj from './kbn_core_deprecations_server.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server_internal.mdx b/api_docs/kbn_core_deprecations_server_internal.mdx index eab385738a1ee..7a5b1cd2bc9fe 100644 --- a/api_docs/kbn_core_deprecations_server_internal.mdx +++ b/api_docs/kbn_core_deprecations_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-internal title: "@kbn/core-deprecations-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server-internal'] --- import kbnCoreDeprecationsServerInternalObj from './kbn_core_deprecations_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server_mocks.mdx b/api_docs/kbn_core_deprecations_server_mocks.mdx index 44260725a5810..3d86a5c005edb 100644 --- a/api_docs/kbn_core_deprecations_server_mocks.mdx +++ b/api_docs/kbn_core_deprecations_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-mocks title: "@kbn/core-deprecations-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server-mocks'] --- import kbnCoreDeprecationsServerMocksObj from './kbn_core_deprecations_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_browser.mdx b/api_docs/kbn_core_doc_links_browser.mdx index 4668d955d2110..1bcbe10f847e2 100644 --- a/api_docs/kbn_core_doc_links_browser.mdx +++ b/api_docs/kbn_core_doc_links_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser title: "@kbn/core-doc-links-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser'] --- import kbnCoreDocLinksBrowserObj from './kbn_core_doc_links_browser.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_browser_mocks.mdx b/api_docs/kbn_core_doc_links_browser_mocks.mdx index 3660d233ad6ab..e6c84e04ff104 100644 --- a/api_docs/kbn_core_doc_links_browser_mocks.mdx +++ b/api_docs/kbn_core_doc_links_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser-mocks title: "@kbn/core-doc-links-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser-mocks'] --- import kbnCoreDocLinksBrowserMocksObj from './kbn_core_doc_links_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_server.mdx b/api_docs/kbn_core_doc_links_server.mdx index df6cf8d20c4ce..25a997e9680ed 100644 --- a/api_docs/kbn_core_doc_links_server.mdx +++ b/api_docs/kbn_core_doc_links_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server title: "@kbn/core-doc-links-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server'] --- import kbnCoreDocLinksServerObj from './kbn_core_doc_links_server.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_server_mocks.mdx b/api_docs/kbn_core_doc_links_server_mocks.mdx index 73c334fed71b7..d69c2d624d041 100644 --- a/api_docs/kbn_core_doc_links_server_mocks.mdx +++ b/api_docs/kbn_core_doc_links_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server-mocks title: "@kbn/core-doc-links-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server-mocks'] --- import kbnCoreDocLinksServerMocksObj from './kbn_core_doc_links_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx index 67f8319478afa..6ec97363c7ac6 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-internal title: "@kbn/core-elasticsearch-client-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-internal'] --- import kbnCoreElasticsearchClientServerInternalObj from './kbn_core_elasticsearch_client_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx index aa578123eaee7..7a35b078c5e1f 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-mocks title: "@kbn/core-elasticsearch-client-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-mocks'] --- import kbnCoreElasticsearchClientServerMocksObj from './kbn_core_elasticsearch_client_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server.devdocs.json b/api_docs/kbn_core_elasticsearch_server.devdocs.json index 6ddcc2349c5b3..b926e48892b73 100644 --- a/api_docs/kbn_core_elasticsearch_server.devdocs.json +++ b/api_docs/kbn_core_elasticsearch_server.devdocs.json @@ -68,6 +68,35 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/core-elasticsearch-server", + "id": "def-common.ElasticsearchCapabilities", + "type": "Interface", + "tags": [], + "label": "ElasticsearchCapabilities", + "description": [ + "\nRepresent the capabilities supported by a given ES cluster.\n" + ], + "path": "packages/core/elasticsearch/core-elasticsearch-server/src/contracts.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-elasticsearch-server", + "id": "def-common.ElasticsearchCapabilities.serverless", + "type": "boolean", + "tags": [], + "label": "serverless", + "description": [ + "\nIndicates whether we're connected to a serverless version of elasticsearch.\nRequired because some options aren't working for serverless and code needs to have the info to react accordingly." + ], + "path": "packages/core/elasticsearch/core-elasticsearch-server/src/contracts.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-elasticsearch-server", "id": "def-common.ElasticsearchClientConfig", @@ -856,6 +885,31 @@ } ], "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-elasticsearch-server", + "id": "def-common.ElasticsearchServiceStart.getCapabilities", + "type": "Function", + "tags": [], + "label": "getCapabilities", + "description": [ + "\nReturns the capabilities for the default cluster." + ], + "signature": [ + "() => ", + { + "pluginId": "@kbn/core-elasticsearch-server", + "scope": "common", + "docId": "kibKbnCoreElasticsearchServerPluginApi", + "section": "def-common.ElasticsearchCapabilities", + "text": "ElasticsearchCapabilities" + } + ], + "path": "packages/core/elasticsearch/core-elasticsearch-server/src/contracts.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] } ], "initialIsOpen": false diff --git a/api_docs/kbn_core_elasticsearch_server.mdx b/api_docs/kbn_core_elasticsearch_server.mdx index 1f415fde69839..995420e9c5e99 100644 --- a/api_docs/kbn_core_elasticsearch_server.mdx +++ b/api_docs/kbn_core_elasticsearch_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server title: "@kbn/core-elasticsearch-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server'] --- import kbnCoreElasticsearchServerObj from './kbn_core_elasticsearch_server.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 108 | 0 | 54 | 0 | +| 111 | 0 | 54 | 0 | ## Common diff --git a/api_docs/kbn_core_elasticsearch_server_internal.devdocs.json b/api_docs/kbn_core_elasticsearch_server_internal.devdocs.json index 02c0c4b3fb2a5..86657554ce3ed 100644 --- a/api_docs/kbn_core_elasticsearch_server_internal.devdocs.json +++ b/api_docs/kbn_core_elasticsearch_server_internal.devdocs.json @@ -1494,6 +1494,17 @@ "path": "packages/core/elasticsearch/core-elasticsearch-server-internal/src/get_cluster_info.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-elasticsearch-server-internal", + "id": "def-common.ClusterInfo.cluster_build_flavor", + "type": "string", + "tags": [], + "label": "cluster_build_flavor", + "description": [], + "path": "packages/core/elasticsearch/core-elasticsearch-server-internal/src/get_cluster_info.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/kbn_core_elasticsearch_server_internal.mdx b/api_docs/kbn_core_elasticsearch_server_internal.mdx index 6d3ba2a4b12d6..b498071c37156 100644 --- a/api_docs/kbn_core_elasticsearch_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-internal title: "@kbn/core-elasticsearch-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-internal'] --- import kbnCoreElasticsearchServerInternalObj from './kbn_core_elasticsearch_server_internal.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 37 | 0 | 33 | 3 | +| 38 | 0 | 34 | 3 | ## Common diff --git a/api_docs/kbn_core_elasticsearch_server_mocks.devdocs.json b/api_docs/kbn_core_elasticsearch_server_mocks.devdocs.json index ce0361d169a13..45ce3bb04cd1e 100644 --- a/api_docs/kbn_core_elasticsearch_server_mocks.devdocs.json +++ b/api_docs/kbn_core_elasticsearch_server_mocks.devdocs.json @@ -19,124 +19,7 @@ "common": { "classes": [], "functions": [], - "interfaces": [ - { - "parentPluginId": "@kbn/core-elasticsearch-server-mocks", - "id": "def-common.MockedElasticSearchServiceStart", - "type": "Interface", - "tags": [], - "label": "MockedElasticSearchServiceStart", - "description": [], - "path": "packages/core/elasticsearch/core-elasticsearch-server-mocks/src/elasticsearch_service.mock.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-elasticsearch-server-mocks", - "id": "def-common.MockedElasticSearchServiceStart.client", - "type": "Object", - "tags": [], - "label": "client", - "description": [], - "signature": [ - { - "pluginId": "@kbn/core-elasticsearch-client-server-mocks", - "scope": "common", - "docId": "kibKbnCoreElasticsearchClientServerMocksPluginApi", - "section": "def-common.ClusterClientMock", - "text": "ClusterClientMock" - } - ], - "path": "packages/core/elasticsearch/core-elasticsearch-server-mocks/src/elasticsearch_service.mock.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/core-elasticsearch-server-mocks", - "id": "def-common.MockedElasticSearchServiceStart.createClient", - "type": "Function", - "tags": [], - "label": "createClient", - "description": [], - "signature": [ - "jest.MockInstance<", - { - "pluginId": "@kbn/core-elasticsearch-client-server-mocks", - "scope": "common", - "docId": "kibKbnCoreElasticsearchClientServerMocksPluginApi", - "section": "def-common.CustomClusterClientMock", - "text": "CustomClusterClientMock" - }, - ", [type: string, config?: Partial<", - { - "pluginId": "@kbn/core-elasticsearch-server", - "scope": "common", - "docId": "kibKbnCoreElasticsearchServerPluginApi", - "section": "def-common.ElasticsearchClientConfig", - "text": "ElasticsearchClientConfig" - }, - "> | undefined], unknown> & ((type: string, config?: Partial<", - { - "pluginId": "@kbn/core-elasticsearch-server", - "scope": "common", - "docId": "kibKbnCoreElasticsearchServerPluginApi", - "section": "def-common.ElasticsearchClientConfig", - "text": "ElasticsearchClientConfig" - }, - "> | undefined) => ", - { - "pluginId": "@kbn/core-elasticsearch-client-server-mocks", - "scope": "common", - "docId": "kibKbnCoreElasticsearchClientServerMocksPluginApi", - "section": "def-common.CustomClusterClientMock", - "text": "CustomClusterClientMock" - }, - ")" - ], - "path": "packages/core/elasticsearch/core-elasticsearch-server-mocks/src/elasticsearch_service.mock.ts", - "deprecated": false, - "trackAdoption": false, - "returnComment": [], - "children": [ - { - "parentPluginId": "@kbn/core-elasticsearch-server-mocks", - "id": "def-common.MockedElasticSearchServiceStart.createClient.$1", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "path": "packages/core/elasticsearch/core-elasticsearch-server-mocks/src/elasticsearch_service.mock.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/core-elasticsearch-server-mocks", - "id": "def-common.MockedElasticSearchServiceStart.createClient.$2", - "type": "Object", - "tags": [], - "label": "config", - "description": [], - "signature": [ - "Partial<", - { - "pluginId": "@kbn/core-elasticsearch-server", - "scope": "common", - "docId": "kibKbnCoreElasticsearchServerPluginApi", - "section": "def-common.ElasticsearchClientConfig", - "text": "ElasticsearchClientConfig" - }, - "> | undefined" - ], - "path": "packages/core/elasticsearch/core-elasticsearch-server-mocks/src/elasticsearch_service.mock.ts", - "deprecated": false, - "trackAdoption": false - } - ] - } - ], - "initialIsOpen": false - } - ], + "interfaces": [], "enums": [], "misc": [ { @@ -173,6 +56,61 @@ "deprecated": false, "trackAdoption": false, "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-elasticsearch-server-mocks", + "id": "def-common.MockedElasticSearchServiceStart", + "type": "Type", + "tags": [], + "label": "MockedElasticSearchServiceStart", + "description": [], + "signature": [ + "{ getCapabilities: jest.MockInstance<", + { + "pluginId": "@kbn/core-elasticsearch-server", + "scope": "common", + "docId": "kibKbnCoreElasticsearchServerPluginApi", + "section": "def-common.ElasticsearchCapabilities", + "text": "ElasticsearchCapabilities" + }, + ", [], unknown>; } & Omit<", + { + "pluginId": "@kbn/core-elasticsearch-server", + "scope": "common", + "docId": "kibKbnCoreElasticsearchServerPluginApi", + "section": "def-common.ElasticsearchServiceStart", + "text": "ElasticsearchServiceStart" + }, + ", \"client\" | \"createClient\"> & { client: ", + { + "pluginId": "@kbn/core-elasticsearch-client-server-mocks", + "scope": "common", + "docId": "kibKbnCoreElasticsearchClientServerMocksPluginApi", + "section": "def-common.ClusterClientMock", + "text": "ClusterClientMock" + }, + "; createClient: jest.MockedFunction<(type: string, config?: Partial<", + { + "pluginId": "@kbn/core-elasticsearch-server", + "scope": "common", + "docId": "kibKbnCoreElasticsearchServerPluginApi", + "section": "def-common.ElasticsearchClientConfig", + "text": "ElasticsearchClientConfig" + }, + "> | undefined) => ", + { + "pluginId": "@kbn/core-elasticsearch-client-server-mocks", + "scope": "common", + "docId": "kibKbnCoreElasticsearchClientServerMocksPluginApi", + "section": "def-common.CustomClusterClientMock", + "text": "CustomClusterClientMock" + }, + ">; }" + ], + "path": "packages/core/elasticsearch/core-elasticsearch-server-mocks/src/elasticsearch_service.mock.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false } ], "objects": [ @@ -320,6 +258,52 @@ "returnComment": [], "children": [] }, + { + "parentPluginId": "@kbn/core-elasticsearch-server-mocks", + "id": "def-common.elasticsearchServiceMock.createCapabilities", + "type": "Function", + "tags": [], + "label": "createCapabilities", + "description": [], + "signature": [ + "(parts?: Partial<", + { + "pluginId": "@kbn/core-elasticsearch-server", + "scope": "common", + "docId": "kibKbnCoreElasticsearchServerPluginApi", + "section": "def-common.ElasticsearchCapabilities", + "text": "ElasticsearchCapabilities" + }, + ">) => ", + { + "pluginId": "@kbn/core-elasticsearch-server", + "scope": "common", + "docId": "kibKbnCoreElasticsearchServerPluginApi", + "section": "def-common.ElasticsearchCapabilities", + "text": "ElasticsearchCapabilities" + } + ], + "path": "packages/core/elasticsearch/core-elasticsearch-server-mocks/src/elasticsearch_service.mock.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/core-elasticsearch-server-mocks", + "id": "def-common.elasticsearchServiceMock.createCapabilities.$1", + "type": "Object", + "tags": [], + "label": "parts", + "description": [], + "signature": [ + "{ serverless?: boolean | undefined; }" + ], + "path": "packages/core/elasticsearch/core-elasticsearch-server-mocks/src/elasticsearch_service.mock.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, { "parentPluginId": "@kbn/core-elasticsearch-server-mocks", "id": "def-common.elasticsearchServiceMock.Unnamed", diff --git a/api_docs/kbn_core_elasticsearch_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_server_mocks.mdx index d80a5ce1ecc32..66af48d6ebf65 100644 --- a/api_docs/kbn_core_elasticsearch_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-mocks title: "@kbn/core-elasticsearch-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-mocks'] --- import kbnCoreElasticsearchServerMocksObj from './kbn_core_elasticsearch_server_mocks.devdocs.json'; @@ -21,16 +21,13 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 15 | 1 | 15 | 0 | +| 13 | 1 | 13 | 0 | ## Common ### Objects -### Interfaces - - ### Consts, variables and types diff --git a/api_docs/kbn_core_environment_server_internal.mdx b/api_docs/kbn_core_environment_server_internal.mdx index 45b635efe6884..5f552f25a35b9 100644 --- a/api_docs/kbn_core_environment_server_internal.mdx +++ b/api_docs/kbn_core_environment_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-internal title: "@kbn/core-environment-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-internal'] --- import kbnCoreEnvironmentServerInternalObj from './kbn_core_environment_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_environment_server_mocks.mdx b/api_docs/kbn_core_environment_server_mocks.mdx index c682d53842de9..ea588dfe4b628 100644 --- a/api_docs/kbn_core_environment_server_mocks.mdx +++ b/api_docs/kbn_core_environment_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-mocks title: "@kbn/core-environment-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-mocks'] --- import kbnCoreEnvironmentServerMocksObj from './kbn_core_environment_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser.mdx b/api_docs/kbn_core_execution_context_browser.mdx index 348c4db0922ce..a737d28921aa6 100644 --- a/api_docs/kbn_core_execution_context_browser.mdx +++ b/api_docs/kbn_core_execution_context_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser title: "@kbn/core-execution-context-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser'] --- import kbnCoreExecutionContextBrowserObj from './kbn_core_execution_context_browser.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser_internal.mdx b/api_docs/kbn_core_execution_context_browser_internal.mdx index 10c7fe0413f46..c595b2bf4aee1 100644 --- a/api_docs/kbn_core_execution_context_browser_internal.mdx +++ b/api_docs/kbn_core_execution_context_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-internal title: "@kbn/core-execution-context-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-internal'] --- import kbnCoreExecutionContextBrowserInternalObj from './kbn_core_execution_context_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser_mocks.mdx b/api_docs/kbn_core_execution_context_browser_mocks.mdx index 29ef2bee4f4c0..e73c5aea252be 100644 --- a/api_docs/kbn_core_execution_context_browser_mocks.mdx +++ b/api_docs/kbn_core_execution_context_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-mocks title: "@kbn/core-execution-context-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-mocks'] --- import kbnCoreExecutionContextBrowserMocksObj from './kbn_core_execution_context_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_common.mdx b/api_docs/kbn_core_execution_context_common.mdx index 74078fc2edd0e..536185db025bf 100644 --- a/api_docs/kbn_core_execution_context_common.mdx +++ b/api_docs/kbn_core_execution_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-common title: "@kbn/core-execution-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-common plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-common'] --- import kbnCoreExecutionContextCommonObj from './kbn_core_execution_context_common.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server.mdx b/api_docs/kbn_core_execution_context_server.mdx index 57502724d1cd3..039183d8996a3 100644 --- a/api_docs/kbn_core_execution_context_server.mdx +++ b/api_docs/kbn_core_execution_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server title: "@kbn/core-execution-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server'] --- import kbnCoreExecutionContextServerObj from './kbn_core_execution_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server_internal.mdx b/api_docs/kbn_core_execution_context_server_internal.mdx index 57a2e8c499247..297c976c40a97 100644 --- a/api_docs/kbn_core_execution_context_server_internal.mdx +++ b/api_docs/kbn_core_execution_context_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-internal title: "@kbn/core-execution-context-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-internal'] --- import kbnCoreExecutionContextServerInternalObj from './kbn_core_execution_context_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server_mocks.mdx b/api_docs/kbn_core_execution_context_server_mocks.mdx index 9620c8e1c53d1..b69d9803864b9 100644 --- a/api_docs/kbn_core_execution_context_server_mocks.mdx +++ b/api_docs/kbn_core_execution_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-mocks title: "@kbn/core-execution-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-mocks'] --- import kbnCoreExecutionContextServerMocksObj from './kbn_core_execution_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_fatal_errors_browser.mdx b/api_docs/kbn_core_fatal_errors_browser.mdx index 2392291ace036..673a4b56a678d 100644 --- a/api_docs/kbn_core_fatal_errors_browser.mdx +++ b/api_docs/kbn_core_fatal_errors_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser title: "@kbn/core-fatal-errors-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser'] --- import kbnCoreFatalErrorsBrowserObj from './kbn_core_fatal_errors_browser.devdocs.json'; diff --git a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx index 8df9c6550b3c4..67af69942d1d0 100644 --- a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx +++ b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser-mocks title: "@kbn/core-fatal-errors-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser-mocks'] --- import kbnCoreFatalErrorsBrowserMocksObj from './kbn_core_fatal_errors_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser.mdx b/api_docs/kbn_core_http_browser.mdx index 1eaefe6491d03..82b2d27bb43f7 100644 --- a/api_docs/kbn_core_http_browser.mdx +++ b/api_docs/kbn_core_http_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser title: "@kbn/core-http-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser'] --- import kbnCoreHttpBrowserObj from './kbn_core_http_browser.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser_internal.mdx b/api_docs/kbn_core_http_browser_internal.mdx index 68e2d489d44f0..e9c22d6a04219 100644 --- a/api_docs/kbn_core_http_browser_internal.mdx +++ b/api_docs/kbn_core_http_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-internal title: "@kbn/core-http-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-internal'] --- import kbnCoreHttpBrowserInternalObj from './kbn_core_http_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser_mocks.mdx b/api_docs/kbn_core_http_browser_mocks.mdx index 6ba7f1277ea89..ddb19fe2acda8 100644 --- a/api_docs/kbn_core_http_browser_mocks.mdx +++ b/api_docs/kbn_core_http_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-mocks title: "@kbn/core-http-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-mocks'] --- import kbnCoreHttpBrowserMocksObj from './kbn_core_http_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_common.mdx b/api_docs/kbn_core_http_common.mdx index 486c5a4d71d90..6e50dfb1e04fc 100644 --- a/api_docs/kbn_core_http_common.mdx +++ b/api_docs/kbn_core_http_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-common title: "@kbn/core-http-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-common plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-common'] --- import kbnCoreHttpCommonObj from './kbn_core_http_common.devdocs.json'; diff --git a/api_docs/kbn_core_http_context_server_mocks.mdx b/api_docs/kbn_core_http_context_server_mocks.mdx index 22ffc84c2abb9..b98cd8e966b79 100644 --- a/api_docs/kbn_core_http_context_server_mocks.mdx +++ b/api_docs/kbn_core_http_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-context-server-mocks title: "@kbn/core-http-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-context-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-context-server-mocks'] --- import kbnCoreHttpContextServerMocksObj from './kbn_core_http_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_request_handler_context_server.mdx b/api_docs/kbn_core_http_request_handler_context_server.mdx index 622b235833656..dac4ffde89734 100644 --- a/api_docs/kbn_core_http_request_handler_context_server.mdx +++ b/api_docs/kbn_core_http_request_handler_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-request-handler-context-server title: "@kbn/core-http-request-handler-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-request-handler-context-server plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-request-handler-context-server'] --- import kbnCoreHttpRequestHandlerContextServerObj from './kbn_core_http_request_handler_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server.mdx b/api_docs/kbn_core_http_resources_server.mdx index b761cab2f7d22..863aff53a998f 100644 --- a/api_docs/kbn_core_http_resources_server.mdx +++ b/api_docs/kbn_core_http_resources_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server title: "@kbn/core-http-resources-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server'] --- import kbnCoreHttpResourcesServerObj from './kbn_core_http_resources_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_internal.mdx b/api_docs/kbn_core_http_resources_server_internal.mdx index d5616e660806b..259b12114cfdf 100644 --- a/api_docs/kbn_core_http_resources_server_internal.mdx +++ b/api_docs/kbn_core_http_resources_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-internal title: "@kbn/core-http-resources-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-internal'] --- import kbnCoreHttpResourcesServerInternalObj from './kbn_core_http_resources_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_mocks.mdx b/api_docs/kbn_core_http_resources_server_mocks.mdx index c956b4a83099a..0ee604bd7228d 100644 --- a/api_docs/kbn_core_http_resources_server_mocks.mdx +++ b/api_docs/kbn_core_http_resources_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-mocks title: "@kbn/core-http-resources-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-mocks'] --- import kbnCoreHttpResourcesServerMocksObj from './kbn_core_http_resources_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_internal.mdx b/api_docs/kbn_core_http_router_server_internal.mdx index 968679c540997..9d0dd58feda1d 100644 --- a/api_docs/kbn_core_http_router_server_internal.mdx +++ b/api_docs/kbn_core_http_router_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-internal title: "@kbn/core-http-router-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-internal'] --- import kbnCoreHttpRouterServerInternalObj from './kbn_core_http_router_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_mocks.mdx b/api_docs/kbn_core_http_router_server_mocks.mdx index c95c5e725901d..4cd12d8e150cd 100644 --- a/api_docs/kbn_core_http_router_server_mocks.mdx +++ b/api_docs/kbn_core_http_router_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-mocks title: "@kbn/core-http-router-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-mocks'] --- import kbnCoreHttpRouterServerMocksObj from './kbn_core_http_router_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_server.devdocs.json b/api_docs/kbn_core_http_server.devdocs.json index efe1548d0c612..c226ad218a529 100644 --- a/api_docs/kbn_core_http_server.devdocs.json +++ b/api_docs/kbn_core_http_server.devdocs.json @@ -5231,6 +5231,10 @@ "plugin": "ecsDataQualityDashboard", "path": "x-pack/plugins/ecs_data_quality_dashboard/server/__mocks__/server.ts" }, + { + "plugin": "elasticAssistant", + "path": "x-pack/plugins/elastic_assistant/server/__mocks__/server.ts" + }, { "plugin": "features", "path": "x-pack/plugins/features/server/routes/index.test.ts" @@ -6281,6 +6285,10 @@ "plugin": "encryptedSavedObjects", "path": "x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.ts" }, + { + "plugin": "serverless", + "path": "x-pack/plugins/serverless/server/plugin.ts" + }, { "plugin": "actions", "path": "x-pack/plugins/actions/server/routes/get_oauth_access_token.ts" @@ -6785,6 +6793,10 @@ "plugin": "ecsDataQualityDashboard", "path": "x-pack/plugins/ecs_data_quality_dashboard/server/routes/get_unallowed_field_values.ts" }, + { + "plugin": "elasticAssistant", + "path": "x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.ts" + }, { "plugin": "globalSearch", "path": "x-pack/plugins/global_search/server/routes/find.ts" @@ -7209,10 +7221,6 @@ "plugin": "searchprofiler", "path": "x-pack/plugins/searchprofiler/server/routes/profile.ts" }, - { - "plugin": "serverless", - "path": "x-pack/plugins/serverless/server/plugin.ts" - }, { "plugin": "snapshotRestore", "path": "x-pack/plugins/snapshot_restore/server/routes/api/repositories.ts" @@ -7817,6 +7825,10 @@ "plugin": "ecsDataQualityDashboard", "path": "x-pack/plugins/ecs_data_quality_dashboard/server/__mocks__/server.ts" }, + { + "plugin": "elasticAssistant", + "path": "x-pack/plugins/elastic_assistant/server/__mocks__/server.ts" + }, { "plugin": "encryptedSavedObjects", "path": "x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.test.ts" @@ -8971,6 +8983,10 @@ "plugin": "ecsDataQualityDashboard", "path": "x-pack/plugins/ecs_data_quality_dashboard/server/__mocks__/server.ts" }, + { + "plugin": "elasticAssistant", + "path": "x-pack/plugins/elastic_assistant/server/__mocks__/server.ts" + }, { "plugin": "globalSearch", "path": "x-pack/plugins/global_search/server/routes/index.test.ts" @@ -9389,6 +9405,10 @@ "plugin": "ecsDataQualityDashboard", "path": "x-pack/plugins/ecs_data_quality_dashboard/server/__mocks__/server.ts" }, + { + "plugin": "elasticAssistant", + "path": "x-pack/plugins/elastic_assistant/server/__mocks__/server.ts" + }, { "plugin": "ruleRegistry", "path": "x-pack/plugins/rule_registry/server/routes/__mocks__/server.ts" @@ -9999,6 +10019,10 @@ "plugin": "ecsDataQualityDashboard", "path": "x-pack/plugins/ecs_data_quality_dashboard/server/__mocks__/server.ts" }, + { + "plugin": "elasticAssistant", + "path": "x-pack/plugins/elastic_assistant/server/__mocks__/server.ts" + }, { "plugin": "globalSearch", "path": "x-pack/plugins/global_search/server/routes/index.test.ts" diff --git a/api_docs/kbn_core_http_server.mdx b/api_docs/kbn_core_http_server.mdx index 8a9fb45c13eea..79d3d870af63e 100644 --- a/api_docs/kbn_core_http_server.mdx +++ b/api_docs/kbn_core_http_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server title: "@kbn/core-http-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server'] --- import kbnCoreHttpServerObj from './kbn_core_http_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_internal.mdx b/api_docs/kbn_core_http_server_internal.mdx index f346e16f4d91e..f7b2e4cfa3a12 100644 --- a/api_docs/kbn_core_http_server_internal.mdx +++ b/api_docs/kbn_core_http_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-internal title: "@kbn/core-http-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-internal'] --- import kbnCoreHttpServerInternalObj from './kbn_core_http_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_mocks.mdx b/api_docs/kbn_core_http_server_mocks.mdx index b942627f971a3..ae1e34308bd69 100644 --- a/api_docs/kbn_core_http_server_mocks.mdx +++ b/api_docs/kbn_core_http_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-mocks title: "@kbn/core-http-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-mocks'] --- import kbnCoreHttpServerMocksObj from './kbn_core_http_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_browser.mdx b/api_docs/kbn_core_i18n_browser.mdx index 1f3504ac14602..cb8e8a6f86d1f 100644 --- a/api_docs/kbn_core_i18n_browser.mdx +++ b/api_docs/kbn_core_i18n_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser title: "@kbn/core-i18n-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser'] --- import kbnCoreI18nBrowserObj from './kbn_core_i18n_browser.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_browser_mocks.mdx b/api_docs/kbn_core_i18n_browser_mocks.mdx index 5c0adbfe02bd2..100ae9f06a288 100644 --- a/api_docs/kbn_core_i18n_browser_mocks.mdx +++ b/api_docs/kbn_core_i18n_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser-mocks title: "@kbn/core-i18n-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser-mocks'] --- import kbnCoreI18nBrowserMocksObj from './kbn_core_i18n_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server.mdx b/api_docs/kbn_core_i18n_server.mdx index 1654e0a959b96..167dbaa327158 100644 --- a/api_docs/kbn_core_i18n_server.mdx +++ b/api_docs/kbn_core_i18n_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server title: "@kbn/core-i18n-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server'] --- import kbnCoreI18nServerObj from './kbn_core_i18n_server.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server_internal.mdx b/api_docs/kbn_core_i18n_server_internal.mdx index 6d82603d0f92c..ce0a40721a1b1 100644 --- a/api_docs/kbn_core_i18n_server_internal.mdx +++ b/api_docs/kbn_core_i18n_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-internal title: "@kbn/core-i18n-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server-internal'] --- import kbnCoreI18nServerInternalObj from './kbn_core_i18n_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server_mocks.mdx b/api_docs/kbn_core_i18n_server_mocks.mdx index c76b85d117b4f..173477df8cba2 100644 --- a/api_docs/kbn_core_i18n_server_mocks.mdx +++ b/api_docs/kbn_core_i18n_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-mocks title: "@kbn/core-i18n-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server-mocks'] --- import kbnCoreI18nServerMocksObj from './kbn_core_i18n_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx index 2a28d7a9521fd..f5ebcf356933e 100644 --- a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx +++ b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser-mocks title: "@kbn/core-injected-metadata-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-injected-metadata-browser-mocks'] --- import kbnCoreInjectedMetadataBrowserMocksObj from './kbn_core_injected_metadata_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_integrations_browser_internal.mdx b/api_docs/kbn_core_integrations_browser_internal.mdx index abe3799d80651..30bd8215d0f93 100644 --- a/api_docs/kbn_core_integrations_browser_internal.mdx +++ b/api_docs/kbn_core_integrations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-internal title: "@kbn/core-integrations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-internal'] --- import kbnCoreIntegrationsBrowserInternalObj from './kbn_core_integrations_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_integrations_browser_mocks.mdx b/api_docs/kbn_core_integrations_browser_mocks.mdx index 32dac0764785c..eaf7505c170a0 100644 --- a/api_docs/kbn_core_integrations_browser_mocks.mdx +++ b/api_docs/kbn_core_integrations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-mocks title: "@kbn/core-integrations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-mocks'] --- import kbnCoreIntegrationsBrowserMocksObj from './kbn_core_integrations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser.mdx b/api_docs/kbn_core_lifecycle_browser.mdx index 43df82357efb8..1076d85474f01 100644 --- a/api_docs/kbn_core_lifecycle_browser.mdx +++ b/api_docs/kbn_core_lifecycle_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser title: "@kbn/core-lifecycle-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser'] --- import kbnCoreLifecycleBrowserObj from './kbn_core_lifecycle_browser.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser_mocks.mdx b/api_docs/kbn_core_lifecycle_browser_mocks.mdx index c22d33d42b5af..45d7b6cd01d36 100644 --- a/api_docs/kbn_core_lifecycle_browser_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser-mocks title: "@kbn/core-lifecycle-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser-mocks'] --- import kbnCoreLifecycleBrowserMocksObj from './kbn_core_lifecycle_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_server.mdx b/api_docs/kbn_core_lifecycle_server.mdx index ceec836e88c39..b6a4f8983cc0a 100644 --- a/api_docs/kbn_core_lifecycle_server.mdx +++ b/api_docs/kbn_core_lifecycle_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server title: "@kbn/core-lifecycle-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-server'] --- import kbnCoreLifecycleServerObj from './kbn_core_lifecycle_server.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_server_mocks.mdx b/api_docs/kbn_core_lifecycle_server_mocks.mdx index a7cdaf3ef18f1..52d3bc6e3141d 100644 --- a/api_docs/kbn_core_lifecycle_server_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server-mocks title: "@kbn/core-lifecycle-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-server-mocks'] --- import kbnCoreLifecycleServerMocksObj from './kbn_core_lifecycle_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_browser_mocks.mdx b/api_docs/kbn_core_logging_browser_mocks.mdx index 2d39bd149c2ea..20f895dc34886 100644 --- a/api_docs/kbn_core_logging_browser_mocks.mdx +++ b/api_docs/kbn_core_logging_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-browser-mocks title: "@kbn/core-logging-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-browser-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-browser-mocks'] --- import kbnCoreLoggingBrowserMocksObj from './kbn_core_logging_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_common_internal.mdx b/api_docs/kbn_core_logging_common_internal.mdx index db5ac2cd1e1cd..dcf3e6e39b6d2 100644 --- a/api_docs/kbn_core_logging_common_internal.mdx +++ b/api_docs/kbn_core_logging_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-common-internal title: "@kbn/core-logging-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-common-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-common-internal'] --- import kbnCoreLoggingCommonInternalObj from './kbn_core_logging_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server.mdx b/api_docs/kbn_core_logging_server.mdx index 2cb148512b9ab..46e71a104d3de 100644 --- a/api_docs/kbn_core_logging_server.mdx +++ b/api_docs/kbn_core_logging_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server title: "@kbn/core-logging-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server'] --- import kbnCoreLoggingServerObj from './kbn_core_logging_server.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server_internal.mdx b/api_docs/kbn_core_logging_server_internal.mdx index 376c0e44f681a..60de046e3cc02 100644 --- a/api_docs/kbn_core_logging_server_internal.mdx +++ b/api_docs/kbn_core_logging_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-internal title: "@kbn/core-logging-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-internal'] --- import kbnCoreLoggingServerInternalObj from './kbn_core_logging_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server_mocks.mdx b/api_docs/kbn_core_logging_server_mocks.mdx index 070bcbbfc8103..b1f39308253e7 100644 --- a/api_docs/kbn_core_logging_server_mocks.mdx +++ b/api_docs/kbn_core_logging_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-mocks title: "@kbn/core-logging-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-mocks'] --- import kbnCoreLoggingServerMocksObj from './kbn_core_logging_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_collectors_server_internal.mdx b/api_docs/kbn_core_metrics_collectors_server_internal.mdx index 13b82b63de789..5e8b3e5a62314 100644 --- a/api_docs/kbn_core_metrics_collectors_server_internal.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-internal title: "@kbn/core-metrics-collectors-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-internal'] --- import kbnCoreMetricsCollectorsServerInternalObj from './kbn_core_metrics_collectors_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx index 325d56ca40e7a..8a3de8307b1f5 100644 --- a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-mocks title: "@kbn/core-metrics-collectors-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-mocks'] --- import kbnCoreMetricsCollectorsServerMocksObj from './kbn_core_metrics_collectors_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server.mdx b/api_docs/kbn_core_metrics_server.mdx index 3940e0eaa33d0..ed3f47b751106 100644 --- a/api_docs/kbn_core_metrics_server.mdx +++ b/api_docs/kbn_core_metrics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server title: "@kbn/core-metrics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server'] --- import kbnCoreMetricsServerObj from './kbn_core_metrics_server.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server_internal.mdx b/api_docs/kbn_core_metrics_server_internal.mdx index 47b57b8420eca..b1fe1cff8e149 100644 --- a/api_docs/kbn_core_metrics_server_internal.mdx +++ b/api_docs/kbn_core_metrics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-internal title: "@kbn/core-metrics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-internal'] --- import kbnCoreMetricsServerInternalObj from './kbn_core_metrics_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server_mocks.mdx b/api_docs/kbn_core_metrics_server_mocks.mdx index c9b55064cca94..612250f053548 100644 --- a/api_docs/kbn_core_metrics_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-mocks title: "@kbn/core-metrics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-mocks'] --- import kbnCoreMetricsServerMocksObj from './kbn_core_metrics_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_mount_utils_browser.mdx b/api_docs/kbn_core_mount_utils_browser.mdx index 0e071dbd81352..60b3d9c3b391b 100644 --- a/api_docs/kbn_core_mount_utils_browser.mdx +++ b/api_docs/kbn_core_mount_utils_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-mount-utils-browser title: "@kbn/core-mount-utils-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-mount-utils-browser plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-mount-utils-browser'] --- import kbnCoreMountUtilsBrowserObj from './kbn_core_mount_utils_browser.devdocs.json'; diff --git a/api_docs/kbn_core_node_server.mdx b/api_docs/kbn_core_node_server.mdx index e1091e70c4a2f..8e5ea4c19615e 100644 --- a/api_docs/kbn_core_node_server.mdx +++ b/api_docs/kbn_core_node_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server title: "@kbn/core-node-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server'] --- import kbnCoreNodeServerObj from './kbn_core_node_server.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_internal.mdx b/api_docs/kbn_core_node_server_internal.mdx index 6455066411edd..b79735594ffb8 100644 --- a/api_docs/kbn_core_node_server_internal.mdx +++ b/api_docs/kbn_core_node_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-internal title: "@kbn/core-node-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-internal'] --- import kbnCoreNodeServerInternalObj from './kbn_core_node_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_mocks.mdx b/api_docs/kbn_core_node_server_mocks.mdx index 36c5177c6c5f3..4ebc8d71ba5a2 100644 --- a/api_docs/kbn_core_node_server_mocks.mdx +++ b/api_docs/kbn_core_node_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-mocks title: "@kbn/core-node-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-mocks'] --- import kbnCoreNodeServerMocksObj from './kbn_core_node_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser.mdx b/api_docs/kbn_core_notifications_browser.mdx index 1b943c4619822..2e1cd32fcb249 100644 --- a/api_docs/kbn_core_notifications_browser.mdx +++ b/api_docs/kbn_core_notifications_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser title: "@kbn/core-notifications-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser'] --- import kbnCoreNotificationsBrowserObj from './kbn_core_notifications_browser.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser_internal.mdx b/api_docs/kbn_core_notifications_browser_internal.mdx index 920a9c6c23a75..37061c402bef7 100644 --- a/api_docs/kbn_core_notifications_browser_internal.mdx +++ b/api_docs/kbn_core_notifications_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-internal title: "@kbn/core-notifications-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-internal'] --- import kbnCoreNotificationsBrowserInternalObj from './kbn_core_notifications_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser_mocks.mdx b/api_docs/kbn_core_notifications_browser_mocks.mdx index 7c6742cb88562..84ae73ec14b4b 100644 --- a/api_docs/kbn_core_notifications_browser_mocks.mdx +++ b/api_docs/kbn_core_notifications_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-mocks title: "@kbn/core-notifications-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-mocks'] --- import kbnCoreNotificationsBrowserMocksObj from './kbn_core_notifications_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser.mdx b/api_docs/kbn_core_overlays_browser.mdx index 777cecaee74bb..b59bbb842eca9 100644 --- a/api_docs/kbn_core_overlays_browser.mdx +++ b/api_docs/kbn_core_overlays_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser title: "@kbn/core-overlays-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser'] --- import kbnCoreOverlaysBrowserObj from './kbn_core_overlays_browser.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser_internal.mdx b/api_docs/kbn_core_overlays_browser_internal.mdx index fa88a92022f08..62aec73fc0457 100644 --- a/api_docs/kbn_core_overlays_browser_internal.mdx +++ b/api_docs/kbn_core_overlays_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-internal title: "@kbn/core-overlays-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-internal'] --- import kbnCoreOverlaysBrowserInternalObj from './kbn_core_overlays_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser_mocks.mdx b/api_docs/kbn_core_overlays_browser_mocks.mdx index 3d478318cf7c4..2b6fa411a1b72 100644 --- a/api_docs/kbn_core_overlays_browser_mocks.mdx +++ b/api_docs/kbn_core_overlays_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-mocks title: "@kbn/core-overlays-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-mocks'] --- import kbnCoreOverlaysBrowserMocksObj from './kbn_core_overlays_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser.mdx b/api_docs/kbn_core_plugins_browser.mdx index 5cdf10f520c37..35eadeade734d 100644 --- a/api_docs/kbn_core_plugins_browser.mdx +++ b/api_docs/kbn_core_plugins_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser title: "@kbn/core-plugins-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser'] --- import kbnCorePluginsBrowserObj from './kbn_core_plugins_browser.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser_mocks.mdx b/api_docs/kbn_core_plugins_browser_mocks.mdx index 046b38fcf8686..d2c80db9d0da8 100644 --- a/api_docs/kbn_core_plugins_browser_mocks.mdx +++ b/api_docs/kbn_core_plugins_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser-mocks title: "@kbn/core-plugins-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser-mocks'] --- import kbnCorePluginsBrowserMocksObj from './kbn_core_plugins_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server.mdx b/api_docs/kbn_core_plugins_server.mdx index 4b7c20cd17749..72fa61e3eeab9 100644 --- a/api_docs/kbn_core_plugins_server.mdx +++ b/api_docs/kbn_core_plugins_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server title: "@kbn/core-plugins-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-server'] --- import kbnCorePluginsServerObj from './kbn_core_plugins_server.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server_mocks.mdx b/api_docs/kbn_core_plugins_server_mocks.mdx index 2bd4ddc48f676..e97a77130be9d 100644 --- a/api_docs/kbn_core_plugins_server_mocks.mdx +++ b/api_docs/kbn_core_plugins_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server-mocks title: "@kbn/core-plugins-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-server-mocks'] --- import kbnCorePluginsServerMocksObj from './kbn_core_plugins_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server.mdx b/api_docs/kbn_core_preboot_server.mdx index affb98e39522c..b649bec5321c4 100644 --- a/api_docs/kbn_core_preboot_server.mdx +++ b/api_docs/kbn_core_preboot_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server title: "@kbn/core-preboot-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server'] --- import kbnCorePrebootServerObj from './kbn_core_preboot_server.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server_mocks.mdx b/api_docs/kbn_core_preboot_server_mocks.mdx index 3495668eb0191..677715f149a8a 100644 --- a/api_docs/kbn_core_preboot_server_mocks.mdx +++ b/api_docs/kbn_core_preboot_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server-mocks title: "@kbn/core-preboot-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server-mocks'] --- import kbnCorePrebootServerMocksObj from './kbn_core_preboot_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_browser_mocks.mdx b/api_docs/kbn_core_rendering_browser_mocks.mdx index bf96641a4a5e7..e8cc53127f6cc 100644 --- a/api_docs/kbn_core_rendering_browser_mocks.mdx +++ b/api_docs/kbn_core_rendering_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-browser-mocks title: "@kbn/core-rendering-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-browser-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-browser-mocks'] --- import kbnCoreRenderingBrowserMocksObj from './kbn_core_rendering_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_internal.mdx b/api_docs/kbn_core_rendering_server_internal.mdx index b72268e069c1d..688f5fa567503 100644 --- a/api_docs/kbn_core_rendering_server_internal.mdx +++ b/api_docs/kbn_core_rendering_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-internal title: "@kbn/core-rendering-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-internal'] --- import kbnCoreRenderingServerInternalObj from './kbn_core_rendering_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_mocks.mdx b/api_docs/kbn_core_rendering_server_mocks.mdx index 2048ab81f46da..f1331870c7f73 100644 --- a/api_docs/kbn_core_rendering_server_mocks.mdx +++ b/api_docs/kbn_core_rendering_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-mocks title: "@kbn/core-rendering-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-mocks'] --- import kbnCoreRenderingServerMocksObj from './kbn_core_rendering_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_root_server_internal.mdx b/api_docs/kbn_core_root_server_internal.mdx index 6f8c09617b104..311ff162c0d0b 100644 --- a/api_docs/kbn_core_root_server_internal.mdx +++ b/api_docs/kbn_core_root_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-root-server-internal title: "@kbn/core-root-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-root-server-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-root-server-internal'] --- import kbnCoreRootServerInternalObj from './kbn_core_root_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_browser.mdx b/api_docs/kbn_core_saved_objects_api_browser.mdx index 677b5c3dbb7bc..eb583fc7b7d80 100644 --- a/api_docs/kbn_core_saved_objects_api_browser.mdx +++ b/api_docs/kbn_core_saved_objects_api_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-browser title: "@kbn/core-saved-objects-api-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-browser plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-browser'] --- import kbnCoreSavedObjectsApiBrowserObj from './kbn_core_saved_objects_api_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server.devdocs.json b/api_docs/kbn_core_saved_objects_api_server.devdocs.json index 528190aba475a..e529c9e80e178 100644 --- a/api_docs/kbn_core_saved_objects_api_server.devdocs.json +++ b/api_docs/kbn_core_saved_objects_api_server.devdocs.json @@ -861,8 +861,8 @@ "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsGetOptions", - "text": "SavedObjectsGetOptions" + "section": "def-common.SavedObjectsResolveOptions", + "text": "SavedObjectsResolveOptions" }, " | undefined) => Promise<", { @@ -909,15 +909,15 @@ "tags": [], "label": "options", "description": [ - "{@link SavedObjectsGetOptions } - options for the bulk resolve operation" + "{@link SavedObjectsResolveOptions } - options for the bulk resolve operation" ], "signature": [ { "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsGetOptions", - "text": "SavedObjectsGetOptions" + "section": "def-common.SavedObjectsResolveOptions", + "text": "SavedObjectsResolveOptions" }, " | undefined" ], @@ -1047,8 +1047,8 @@ "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsGetOptions", - "text": "SavedObjectsGetOptions" + "section": "def-common.SavedObjectsResolveOptions", + "text": "SavedObjectsResolveOptions" }, " | undefined) => Promise<", { @@ -1105,15 +1105,15 @@ "tags": [], "label": "options", "description": [ - "{@link SavedObjectsGetOptions } - options for the resolve operation" + "{@link SavedObjectsResolveOptions } - options for the resolve operation" ], "signature": [ { "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsGetOptions", - "text": "SavedObjectsGetOptions" + "section": "def-common.SavedObjectsResolveOptions", + "text": "SavedObjectsResolveOptions" }, " | undefined" ], @@ -4819,8 +4819,8 @@ "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsGetOptions", - "text": "SavedObjectsGetOptions" + "section": "def-common.SavedObjectsResolveOptions", + "text": "SavedObjectsResolveOptions" }, " | undefined) => Promise<", { @@ -4867,15 +4867,15 @@ "tags": [], "label": "options", "description": [ - "{@link SavedObjectsGetOptions } - options for the bulk resolve operation" + "{@link SavedObjectsResolveOptions } - options for the bulk resolve operation" ], "signature": [ { "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsGetOptions", - "text": "SavedObjectsGetOptions" + "section": "def-common.SavedObjectsResolveOptions", + "text": "SavedObjectsResolveOptions" }, " | undefined" ], @@ -4904,8 +4904,8 @@ "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsGetOptions", - "text": "SavedObjectsGetOptions" + "section": "def-common.SavedObjectsResolveOptions", + "text": "SavedObjectsResolveOptions" }, " | undefined) => Promise<", { @@ -4962,15 +4962,15 @@ "tags": [], "label": "options", "description": [ - "{@link SavedObjectsGetOptions } - options for the resolve operation" + "{@link SavedObjectsResolveOptions } - options for the resolve operation" ], "signature": [ { "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsGetOptions", - "text": "SavedObjectsGetOptions" + "section": "def-common.SavedObjectsResolveOptions", + "text": "SavedObjectsResolveOptions" }, " | undefined" ], @@ -6829,20 +6829,6 @@ "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts", "deprecated": false, "trackAdoption": false - }, - { - "parentPluginId": "@kbn/core-saved-objects-api-server", - "id": "def-common.SavedObjectsFindOptions.downwardConversion", - "type": "CompoundType", - "tags": [], - "label": "downwardConversion", - "description": [], - "signature": [ - "\"allow\" | \"forbid\" | undefined" - ], - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts", - "deprecated": false, - "trackAdoption": false } ], "initialIsOpen": false @@ -7118,20 +7104,6 @@ "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/get.ts", "deprecated": false, "trackAdoption": false - }, - { - "parentPluginId": "@kbn/core-saved-objects-api-server", - "id": "def-common.SavedObjectsGetOptions.downwardConversion", - "type": "CompoundType", - "tags": [], - "label": "downwardConversion", - "description": [], - "signature": [ - "\"allow\" | \"forbid\" | undefined" - ], - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/get.ts", - "deprecated": false, - "trackAdoption": false } ], "initialIsOpen": false @@ -7569,6 +7541,55 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObjectsResolveOptions", + "type": "Interface", + "tags": [], + "label": "SavedObjectsResolveOptions", + "description": [ + "\nOptions for the saved objects get operation\n" + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsResolveOptions", + "text": "SavedObjectsResolveOptions" + }, + " extends ", + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsBaseOptions", + "text": "SavedObjectsBaseOptions" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/resolve.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObjectsResolveOptions.migrationVersionCompatibility", + "type": "CompoundType", + "tags": [], + "label": "migrationVersionCompatibility", + "description": [ + "{@link SavedObjectsRawDocParseOptions.migrationVersionCompatibility}" + ], + "signature": [ + "\"raw\" | \"compatible\" | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/resolve.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-saved-objects-api-server", "id": "def-common.SavedObjectsResolveResponse", @@ -8230,7 +8251,7 @@ "section": "def-common.SavedObjectsFindOptionsReference", "text": "SavedObjectsFindOptionsReference" }, - "[] | undefined; hasNoReferenceOperator?: \"AND\" | \"OR\" | undefined; typeToNamespacesMap?: Map | undefined; migrationVersionCompatibility?: \"raw\" | \"compatible\" | undefined; downwardConversion?: \"allow\" | \"forbid\" | undefined; }" + "[] | undefined; hasNoReferenceOperator?: \"AND\" | \"OR\" | undefined; typeToNamespacesMap?: Map | undefined; migrationVersionCompatibility?: \"raw\" | \"compatible\" | undefined; }" ], "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/create_point_in_time_finder.ts", "deprecated": false, diff --git a/api_docs/kbn_core_saved_objects_api_server.mdx b/api_docs/kbn_core_saved_objects_api_server.mdx index 076e898c16b5d..227d0db50d4b0 100644 --- a/api_docs/kbn_core_saved_objects_api_server.mdx +++ b/api_docs/kbn_core_saved_objects_api_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server title: "@kbn/core-saved-objects-api-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server'] --- import kbnCoreSavedObjectsApiServerObj from './kbn_core_saved_objects_api_server.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 351 | 1 | 7 | 1 | +| 351 | 1 | 5 | 1 | ## Common diff --git a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx index 5f626e9595f2d..2e5d85dcd548c 100644 --- a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-mocks title: "@kbn/core-saved-objects-api-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-mocks'] --- import kbnCoreSavedObjectsApiServerMocksObj from './kbn_core_saved_objects_api_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_internal.mdx b/api_docs/kbn_core_saved_objects_base_server_internal.mdx index d7ca06535508b..b183c5413a5a0 100644 --- a/api_docs/kbn_core_saved_objects_base_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-internal title: "@kbn/core-saved-objects-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-internal'] --- import kbnCoreSavedObjectsBaseServerInternalObj from './kbn_core_saved_objects_base_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx index dc2cc0d340343..dcd89040b2edf 100644 --- a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-mocks title: "@kbn/core-saved-objects-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-mocks'] --- import kbnCoreSavedObjectsBaseServerMocksObj from './kbn_core_saved_objects_base_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser.mdx b/api_docs/kbn_core_saved_objects_browser.mdx index 08ef3e09374fb..7863ce545e938 100644 --- a/api_docs/kbn_core_saved_objects_browser.mdx +++ b/api_docs/kbn_core_saved_objects_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser title: "@kbn/core-saved-objects-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser'] --- import kbnCoreSavedObjectsBrowserObj from './kbn_core_saved_objects_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser_internal.mdx b/api_docs/kbn_core_saved_objects_browser_internal.mdx index 1c2697e463450..de37144affb42 100644 --- a/api_docs/kbn_core_saved_objects_browser_internal.mdx +++ b/api_docs/kbn_core_saved_objects_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-internal title: "@kbn/core-saved-objects-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-internal'] --- import kbnCoreSavedObjectsBrowserInternalObj from './kbn_core_saved_objects_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser_mocks.mdx b/api_docs/kbn_core_saved_objects_browser_mocks.mdx index 31d1ffa19b5ef..5c1d1bd8b2fb7 100644 --- a/api_docs/kbn_core_saved_objects_browser_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-mocks title: "@kbn/core-saved-objects-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-mocks'] --- import kbnCoreSavedObjectsBrowserMocksObj from './kbn_core_saved_objects_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_common.mdx b/api_docs/kbn_core_saved_objects_common.mdx index a4c541f9d73bb..b254e4c33dd60 100644 --- a/api_docs/kbn_core_saved_objects_common.mdx +++ b/api_docs/kbn_core_saved_objects_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-common title: "@kbn/core-saved-objects-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-common plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-common'] --- import kbnCoreSavedObjectsCommonObj from './kbn_core_saved_objects_common.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx index fce07b5b6a3b5..f085a159d2dd3 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-internal title: "@kbn/core-saved-objects-import-export-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-internal'] --- import kbnCoreSavedObjectsImportExportServerInternalObj from './kbn_core_saved_objects_import_export_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx index bd8023a234f67..1ae599546797c 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-mocks title: "@kbn/core-saved-objects-import-export-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-mocks'] --- import kbnCoreSavedObjectsImportExportServerMocksObj from './kbn_core_saved_objects_import_export_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_internal.devdocs.json b/api_docs/kbn_core_saved_objects_migration_server_internal.devdocs.json index cbd8b5774457d..4788fa89a6be4 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_internal.devdocs.json +++ b/api_docs/kbn_core_saved_objects_migration_server_internal.devdocs.json @@ -330,7 +330,7 @@ "id": "def-common.KibanaMigrator.Unnamed.$1", "type": "Object", "tags": [], - "label": "{\n client,\n typeRegistry,\n kibanaIndex,\n defaultIndexTypesMap,\n soMigrationsConfig,\n kibanaVersion,\n logger,\n docLinks,\n waitForMigrationCompletion,\n nodeRoles,\n }", + "label": "{\n client,\n typeRegistry,\n kibanaIndex,\n defaultIndexTypesMap,\n soMigrationsConfig,\n kibanaVersion,\n logger,\n docLinks,\n waitForMigrationCompletion,\n nodeRoles,\n esCapabilities,\n }", "description": [], "signature": [ { @@ -831,7 +831,7 @@ "\nMakes a clone of the source index into the target.\n" ], "signature": [ - "({ client, source, target, timeout, }: ", + "({ client, esCapabilities, source, target, timeout, }: ", "CloneIndexParams", ") => ", "TaskEither", @@ -856,7 +856,7 @@ "id": "def-common.cloneIndex.$1", "type": "Object", "tags": [], - "label": "{\n client,\n source,\n target,\n timeout = DEFAULT_TIMEOUT,\n}", + "label": "{\n client,\n esCapabilities,\n source,\n target,\n timeout = DEFAULT_TIMEOUT,\n}", "description": [], "signature": [ "CloneIndexParams" @@ -1020,7 +1020,7 @@ "\nCreates an index with the given mappings\n" ], "signature": [ - "({ client, indexName, mappings, aliases, timeout, }: ", + "({ client, indexName, mappings, esCapabilities, aliases, timeout, }: ", "CreateIndexParams", ") => ", "TaskEither", @@ -1043,7 +1043,7 @@ "id": "def-common.createIndex.$1", "type": "Object", "tags": [], - "label": "{\n client,\n indexName,\n mappings,\n aliases = [],\n timeout = DEFAULT_TIMEOUT,\n}", + "label": "{\n client,\n indexName,\n mappings,\n esCapabilities,\n aliases = [],\n timeout = DEFAULT_TIMEOUT,\n}", "description": [], "signature": [ "CreateIndexParams" @@ -3648,6 +3648,26 @@ "path": "packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-migration-server-internal", + "id": "def-common.KibanaMigratorOptions.esCapabilities", + "type": "Object", + "tags": [], + "label": "esCapabilities", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-elasticsearch-server", + "scope": "common", + "docId": "kibKbnCoreElasticsearchServerPluginApi", + "section": "def-common.ElasticsearchCapabilities", + "text": "ElasticsearchCapabilities" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx index 144ddb38bf147..6e2c6c18a2244 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-internal title: "@kbn/core-saved-objects-migration-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-internal'] --- import kbnCoreSavedObjectsMigrationServerInternalObj from './kbn_core_saved_objects_migration_server_internal.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 124 | 0 | 90 | 46 | +| 125 | 0 | 91 | 46 | ## Common diff --git a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx index 5cdcb5c0b88af..b2596036f146f 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-mocks title: "@kbn/core-saved-objects-migration-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-mocks'] --- import kbnCoreSavedObjectsMigrationServerMocksObj from './kbn_core_saved_objects_migration_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server.mdx b/api_docs/kbn_core_saved_objects_server.mdx index 02433362bdf92..2a128edbd4123 100644 --- a/api_docs/kbn_core_saved_objects_server.mdx +++ b/api_docs/kbn_core_saved_objects_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server title: "@kbn/core-saved-objects-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server'] --- import kbnCoreSavedObjectsServerObj from './kbn_core_saved_objects_server.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server_internal.mdx b/api_docs/kbn_core_saved_objects_server_internal.mdx index 43b57dd232a11..961ee502dba74 100644 --- a/api_docs/kbn_core_saved_objects_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-internal title: "@kbn/core-saved-objects-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server-internal'] --- import kbnCoreSavedObjectsServerInternalObj from './kbn_core_saved_objects_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server_mocks.mdx b/api_docs/kbn_core_saved_objects_server_mocks.mdx index f74e8a9ed0bce..1e8fde889c422 100644 --- a/api_docs/kbn_core_saved_objects_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-mocks title: "@kbn/core-saved-objects-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server-mocks'] --- import kbnCoreSavedObjectsServerMocksObj from './kbn_core_saved_objects_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_utils_server.mdx b/api_docs/kbn_core_saved_objects_utils_server.mdx index 7eee2af89cf69..b9694e3000853 100644 --- a/api_docs/kbn_core_saved_objects_utils_server.mdx +++ b/api_docs/kbn_core_saved_objects_utils_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-utils-server title: "@kbn/core-saved-objects-utils-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-utils-server plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-utils-server'] --- import kbnCoreSavedObjectsUtilsServerObj from './kbn_core_saved_objects_utils_server.devdocs.json'; diff --git a/api_docs/kbn_core_status_common.mdx b/api_docs/kbn_core_status_common.mdx index 8fb781db10df4..6f9dfeb432340 100644 --- a/api_docs/kbn_core_status_common.mdx +++ b/api_docs/kbn_core_status_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common title: "@kbn/core-status-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common'] --- import kbnCoreStatusCommonObj from './kbn_core_status_common.devdocs.json'; diff --git a/api_docs/kbn_core_status_common_internal.mdx b/api_docs/kbn_core_status_common_internal.mdx index eb662bba8bf8c..0b254f28a4e39 100644 --- a/api_docs/kbn_core_status_common_internal.mdx +++ b/api_docs/kbn_core_status_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common-internal title: "@kbn/core-status-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common-internal'] --- import kbnCoreStatusCommonInternalObj from './kbn_core_status_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server.mdx b/api_docs/kbn_core_status_server.mdx index 1e8bb7998f3cc..33b50a3171712 100644 --- a/api_docs/kbn_core_status_server.mdx +++ b/api_docs/kbn_core_status_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server title: "@kbn/core-status-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server'] --- import kbnCoreStatusServerObj from './kbn_core_status_server.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_internal.mdx b/api_docs/kbn_core_status_server_internal.mdx index 995b05dae97e6..d1618d4918996 100644 --- a/api_docs/kbn_core_status_server_internal.mdx +++ b/api_docs/kbn_core_status_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-internal title: "@kbn/core-status-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-internal'] --- import kbnCoreStatusServerInternalObj from './kbn_core_status_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_mocks.mdx b/api_docs/kbn_core_status_server_mocks.mdx index 83050864ff75e..758aa4c4bc691 100644 --- a/api_docs/kbn_core_status_server_mocks.mdx +++ b/api_docs/kbn_core_status_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-mocks title: "@kbn/core-status-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-mocks'] --- import kbnCoreStatusServerMocksObj from './kbn_core_status_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx index 4f191c36b23bd..84c94392b87a5 100644 --- a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx +++ b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-deprecations-getters title: "@kbn/core-test-helpers-deprecations-getters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-deprecations-getters plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-deprecations-getters'] --- import kbnCoreTestHelpersDeprecationsGettersObj from './kbn_core_test_helpers_deprecations_getters.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx index b56bc7e9ee221..8ce8eb614334c 100644 --- a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx +++ b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-http-setup-browser title: "@kbn/core-test-helpers-http-setup-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-http-setup-browser plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-http-setup-browser'] --- import kbnCoreTestHelpersHttpSetupBrowserObj from './kbn_core_test_helpers_http_setup_browser.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_kbn_server.devdocs.json b/api_docs/kbn_core_test_helpers_kbn_server.devdocs.json index f410d08aa0a63..6631202ad7012 100644 --- a/api_docs/kbn_core_test_helpers_kbn_server.devdocs.json +++ b/api_docs/kbn_core_test_helpers_kbn_server.devdocs.json @@ -765,7 +765,17 @@ "label": "TestServerlessESUtils", "description": [], "signature": [ - "{ stop: () => Promise; es: any; }" + "Pick<", + { + "pluginId": "@kbn/core-test-helpers-kbn-server", + "scope": "common", + "docId": "kibKbnCoreTestHelpersKbnServerPluginApi", + "section": "def-common.TestElasticsearchUtils", + "text": "TestElasticsearchUtils" + }, + ", \"stop\" | \"es\"> & { getClient: () => ", + "default", + "; }" ], "path": "packages/core/test-helpers/core-test-helpers-kbn-server/src/create_serverless_root.ts", "deprecated": false, diff --git a/api_docs/kbn_core_test_helpers_kbn_server.mdx b/api_docs/kbn_core_test_helpers_kbn_server.mdx index fe61d4057e77d..9b09ce92e443b 100644 --- a/api_docs/kbn_core_test_helpers_kbn_server.mdx +++ b/api_docs/kbn_core_test_helpers_kbn_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-kbn-server title: "@kbn/core-test-helpers-kbn-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-kbn-server plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-kbn-server'] --- import kbnCoreTestHelpersKbnServerObj from './kbn_core_test_helpers_kbn_server.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx index c7e9f12f428fc..7834a214aa798 100644 --- a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx +++ b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-so-type-serializer title: "@kbn/core-test-helpers-so-type-serializer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-so-type-serializer plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-so-type-serializer'] --- import kbnCoreTestHelpersSoTypeSerializerObj from './kbn_core_test_helpers_so_type_serializer.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_test_utils.mdx b/api_docs/kbn_core_test_helpers_test_utils.mdx index a5a46d51ed6a7..55d2d369ac626 100644 --- a/api_docs/kbn_core_test_helpers_test_utils.mdx +++ b/api_docs/kbn_core_test_helpers_test_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-test-utils title: "@kbn/core-test-helpers-test-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-test-utils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-test-utils'] --- import kbnCoreTestHelpersTestUtilsObj from './kbn_core_test_helpers_test_utils.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser.mdx b/api_docs/kbn_core_theme_browser.mdx index 568918e54edcc..9bbbf78c7f64f 100644 --- a/api_docs/kbn_core_theme_browser.mdx +++ b/api_docs/kbn_core_theme_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser title: "@kbn/core-theme-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser'] --- import kbnCoreThemeBrowserObj from './kbn_core_theme_browser.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index 103d710b3039e..9eea1301252db 100644 --- a/api_docs/kbn_core_theme_browser_mocks.mdx +++ b/api_docs/kbn_core_theme_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-mocks title: "@kbn/core-theme-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-mocks'] --- import kbnCoreThemeBrowserMocksObj from './kbn_core_theme_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser.mdx b/api_docs/kbn_core_ui_settings_browser.mdx index bd23cb655cf19..8d3a5287f8438 100644 --- a/api_docs/kbn_core_ui_settings_browser.mdx +++ b/api_docs/kbn_core_ui_settings_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser title: "@kbn/core-ui-settings-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser'] --- import kbnCoreUiSettingsBrowserObj from './kbn_core_ui_settings_browser.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser_internal.mdx b/api_docs/kbn_core_ui_settings_browser_internal.mdx index 5a90e562b0bc2..85f6df26b9788 100644 --- a/api_docs/kbn_core_ui_settings_browser_internal.mdx +++ b/api_docs/kbn_core_ui_settings_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-internal title: "@kbn/core-ui-settings-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-internal'] --- import kbnCoreUiSettingsBrowserInternalObj from './kbn_core_ui_settings_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser_mocks.mdx b/api_docs/kbn_core_ui_settings_browser_mocks.mdx index c0274566c0578..03185781325d5 100644 --- a/api_docs/kbn_core_ui_settings_browser_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-mocks title: "@kbn/core-ui-settings-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-mocks'] --- import kbnCoreUiSettingsBrowserMocksObj from './kbn_core_ui_settings_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_common.mdx b/api_docs/kbn_core_ui_settings_common.mdx index 7e346176be3a2..d9212725166a6 100644 --- a/api_docs/kbn_core_ui_settings_common.mdx +++ b/api_docs/kbn_core_ui_settings_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-common title: "@kbn/core-ui-settings-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-common plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-common'] --- import kbnCoreUiSettingsCommonObj from './kbn_core_ui_settings_common.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server.mdx b/api_docs/kbn_core_ui_settings_server.mdx index 6a8b1d5c4d767..140be2bd66784 100644 --- a/api_docs/kbn_core_ui_settings_server.mdx +++ b/api_docs/kbn_core_ui_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server title: "@kbn/core-ui-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server'] --- import kbnCoreUiSettingsServerObj from './kbn_core_ui_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_internal.mdx b/api_docs/kbn_core_ui_settings_server_internal.mdx index 1f2281083110e..f77bcf74e7190 100644 --- a/api_docs/kbn_core_ui_settings_server_internal.mdx +++ b/api_docs/kbn_core_ui_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-internal title: "@kbn/core-ui-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-internal'] --- import kbnCoreUiSettingsServerInternalObj from './kbn_core_ui_settings_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_mocks.mdx b/api_docs/kbn_core_ui_settings_server_mocks.mdx index d2a78950e0d9c..83103ed73dddc 100644 --- a/api_docs/kbn_core_ui_settings_server_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-mocks title: "@kbn/core-ui-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-mocks'] --- import kbnCoreUiSettingsServerMocksObj from './kbn_core_ui_settings_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server.mdx b/api_docs/kbn_core_usage_data_server.mdx index e33be649e1953..b1e5842cc38be 100644 --- a/api_docs/kbn_core_usage_data_server.mdx +++ b/api_docs/kbn_core_usage_data_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server title: "@kbn/core-usage-data-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server'] --- import kbnCoreUsageDataServerObj from './kbn_core_usage_data_server.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_internal.mdx b/api_docs/kbn_core_usage_data_server_internal.mdx index 2b4d5d81c7706..68a5256f3d9ed 100644 --- a/api_docs/kbn_core_usage_data_server_internal.mdx +++ b/api_docs/kbn_core_usage_data_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-internal title: "@kbn/core-usage-data-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-internal'] --- import kbnCoreUsageDataServerInternalObj from './kbn_core_usage_data_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_mocks.mdx b/api_docs/kbn_core_usage_data_server_mocks.mdx index 49ab6ce0f6cce..6ac2cfa27b5ff 100644 --- a/api_docs/kbn_core_usage_data_server_mocks.mdx +++ b/api_docs/kbn_core_usage_data_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-mocks title: "@kbn/core-usage-data-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-mocks'] --- import kbnCoreUsageDataServerMocksObj from './kbn_core_usage_data_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server.mdx b/api_docs/kbn_core_user_settings_server.mdx index 004ade62a70e0..bcca65011f8e8 100644 --- a/api_docs/kbn_core_user_settings_server.mdx +++ b/api_docs/kbn_core_user_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server title: "@kbn/core-user-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server'] --- import kbnCoreUserSettingsServerObj from './kbn_core_user_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server_internal.mdx b/api_docs/kbn_core_user_settings_server_internal.mdx index a471a3de444a0..1a82d2fdfcd13 100644 --- a/api_docs/kbn_core_user_settings_server_internal.mdx +++ b/api_docs/kbn_core_user_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server-internal title: "@kbn/core-user-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server-internal plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server-internal'] --- import kbnCoreUserSettingsServerInternalObj from './kbn_core_user_settings_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server_mocks.mdx b/api_docs/kbn_core_user_settings_server_mocks.mdx index 0ff55f98614af..e0bab56d5c1db 100644 --- a/api_docs/kbn_core_user_settings_server_mocks.mdx +++ b/api_docs/kbn_core_user_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server-mocks title: "@kbn/core-user-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server-mocks'] --- import kbnCoreUserSettingsServerMocksObj from './kbn_core_user_settings_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_crypto.mdx b/api_docs/kbn_crypto.mdx index 3986e0b184454..efd2c9d43c394 100644 --- a/api_docs/kbn_crypto.mdx +++ b/api_docs/kbn_crypto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto title: "@kbn/crypto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto'] --- import kbnCryptoObj from './kbn_crypto.devdocs.json'; diff --git a/api_docs/kbn_crypto_browser.mdx b/api_docs/kbn_crypto_browser.mdx index 192a2eae62295..f030b08f89ade 100644 --- a/api_docs/kbn_crypto_browser.mdx +++ b/api_docs/kbn_crypto_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto-browser title: "@kbn/crypto-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto-browser plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] --- import kbnCryptoBrowserObj from './kbn_crypto_browser.devdocs.json'; diff --git a/api_docs/kbn_cypress_config.mdx b/api_docs/kbn_cypress_config.mdx index 6573df31cae87..12a99cec6f65f 100644 --- a/api_docs/kbn_cypress_config.mdx +++ b/api_docs/kbn_cypress_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cypress-config title: "@kbn/cypress-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cypress-config plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cypress-config'] --- import kbnCypressConfigObj from './kbn_cypress_config.devdocs.json'; diff --git a/api_docs/kbn_data_service.mdx b/api_docs/kbn_data_service.mdx index 4822b06f83a15..ee54819beba99 100644 --- a/api_docs/kbn_data_service.mdx +++ b/api_docs/kbn_data_service.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-service title: "@kbn/data-service" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-service plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-service'] --- import kbnDataServiceObj from './kbn_data_service.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index 7c1da975e14b4..4efc8ace6cee1 100644 --- a/api_docs/kbn_datemath.mdx +++ b/api_docs/kbn_datemath.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-datemath title: "@kbn/datemath" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/datemath plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] --- import kbnDatemathObj from './kbn_datemath.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_analytics.mdx b/api_docs/kbn_deeplinks_analytics.mdx index a367ab49636d8..4f8ab53a1157d 100644 --- a/api_docs/kbn_deeplinks_analytics.mdx +++ b/api_docs/kbn_deeplinks_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-analytics title: "@kbn/deeplinks-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-analytics plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-analytics'] --- import kbnDeeplinksAnalyticsObj from './kbn_deeplinks_analytics.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_devtools.mdx b/api_docs/kbn_deeplinks_devtools.mdx index 0ef8243192606..a1eeb9eae6a23 100644 --- a/api_docs/kbn_deeplinks_devtools.mdx +++ b/api_docs/kbn_deeplinks_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-devtools title: "@kbn/deeplinks-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-devtools plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-devtools'] --- import kbnDeeplinksDevtoolsObj from './kbn_deeplinks_devtools.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_management.mdx b/api_docs/kbn_deeplinks_management.mdx index dd681b68b9de9..9518e124a176f 100644 --- a/api_docs/kbn_deeplinks_management.mdx +++ b/api_docs/kbn_deeplinks_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-management title: "@kbn/deeplinks-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-management plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-management'] --- import kbnDeeplinksManagementObj from './kbn_deeplinks_management.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_ml.mdx b/api_docs/kbn_deeplinks_ml.mdx index 6aabb6cefd86e..b7650c5c5dd4a 100644 --- a/api_docs/kbn_deeplinks_ml.mdx +++ b/api_docs/kbn_deeplinks_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-ml title: "@kbn/deeplinks-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-ml plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-ml'] --- import kbnDeeplinksMlObj from './kbn_deeplinks_ml.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_observability.mdx b/api_docs/kbn_deeplinks_observability.mdx index d6d9c2b073b13..dddc0b51d2f30 100644 --- a/api_docs/kbn_deeplinks_observability.mdx +++ b/api_docs/kbn_deeplinks_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-observability title: "@kbn/deeplinks-observability" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-observability plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-observability'] --- import kbnDeeplinksObservabilityObj from './kbn_deeplinks_observability.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_search.mdx b/api_docs/kbn_deeplinks_search.mdx index d26e01786fc5c..eeb4a33a08856 100644 --- a/api_docs/kbn_deeplinks_search.mdx +++ b/api_docs/kbn_deeplinks_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-search title: "@kbn/deeplinks-search" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-search plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-search'] --- import kbnDeeplinksSearchObj from './kbn_deeplinks_search.devdocs.json'; diff --git a/api_docs/kbn_default_nav_analytics.mdx b/api_docs/kbn_default_nav_analytics.mdx index 45629619398b1..26dc3c42e6de3 100644 --- a/api_docs/kbn_default_nav_analytics.mdx +++ b/api_docs/kbn_default_nav_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-analytics title: "@kbn/default-nav-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-analytics plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-analytics'] --- import kbnDefaultNavAnalyticsObj from './kbn_default_nav_analytics.devdocs.json'; diff --git a/api_docs/kbn_default_nav_devtools.mdx b/api_docs/kbn_default_nav_devtools.mdx index 8ac8d0eba5c3a..2944b490bc4b8 100644 --- a/api_docs/kbn_default_nav_devtools.mdx +++ b/api_docs/kbn_default_nav_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-devtools title: "@kbn/default-nav-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-devtools plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-devtools'] --- import kbnDefaultNavDevtoolsObj from './kbn_default_nav_devtools.devdocs.json'; diff --git a/api_docs/kbn_default_nav_management.mdx b/api_docs/kbn_default_nav_management.mdx index c2a57883ee3a0..bfa83cfa31a60 100644 --- a/api_docs/kbn_default_nav_management.mdx +++ b/api_docs/kbn_default_nav_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-management title: "@kbn/default-nav-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-management plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-management'] --- import kbnDefaultNavManagementObj from './kbn_default_nav_management.devdocs.json'; diff --git a/api_docs/kbn_default_nav_ml.mdx b/api_docs/kbn_default_nav_ml.mdx index 416047cecf36f..b2cf182a214a2 100644 --- a/api_docs/kbn_default_nav_ml.mdx +++ b/api_docs/kbn_default_nav_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-ml title: "@kbn/default-nav-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-ml plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-ml'] --- import kbnDefaultNavMlObj from './kbn_default_nav_ml.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx index 7e6a348446ee5..959b1748c1f23 100644 --- a/api_docs/kbn_dev_cli_errors.mdx +++ b/api_docs/kbn_dev_cli_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-errors title: "@kbn/dev-cli-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-errors plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-errors'] --- import kbnDevCliErrorsObj from './kbn_dev_cli_errors.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_runner.mdx b/api_docs/kbn_dev_cli_runner.mdx index e4a85992d8e58..8b3c4efad36cd 100644 --- a/api_docs/kbn_dev_cli_runner.mdx +++ b/api_docs/kbn_dev_cli_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-runner title: "@kbn/dev-cli-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-runner plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-runner'] --- import kbnDevCliRunnerObj from './kbn_dev_cli_runner.devdocs.json'; diff --git a/api_docs/kbn_dev_proc_runner.mdx b/api_docs/kbn_dev_proc_runner.mdx index 9309d75d11b2c..073bbfa04161e 100644 --- a/api_docs/kbn_dev_proc_runner.mdx +++ b/api_docs/kbn_dev_proc_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-proc-runner title: "@kbn/dev-proc-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-proc-runner plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-proc-runner'] --- import kbnDevProcRunnerObj from './kbn_dev_proc_runner.devdocs.json'; diff --git a/api_docs/kbn_dev_utils.mdx b/api_docs/kbn_dev_utils.mdx index 33dda7786d02f..522d693aee8f3 100644 --- a/api_docs/kbn_dev_utils.mdx +++ b/api_docs/kbn_dev_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-utils title: "@kbn/dev-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-utils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_discover_utils.mdx b/api_docs/kbn_discover_utils.mdx index f002c66fb7a5d..e273816a2cf47 100644 --- a/api_docs/kbn_discover_utils.mdx +++ b/api_docs/kbn_discover_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-discover-utils title: "@kbn/discover-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/discover-utils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/discover-utils'] --- import kbnDiscoverUtilsObj from './kbn_discover_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index 263cba94f29c2..c1eddc858e8b6 100644 --- a/api_docs/kbn_doc_links.mdx +++ b/api_docs/kbn_doc_links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-doc-links title: "@kbn/doc-links" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/doc-links plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/doc-links'] --- import kbnDocLinksObj from './kbn_doc_links.devdocs.json'; diff --git a/api_docs/kbn_docs_utils.mdx b/api_docs/kbn_docs_utils.mdx index 1ac93e660a366..3f6406ba9e2ae 100644 --- a/api_docs/kbn_docs_utils.mdx +++ b/api_docs/kbn_docs_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-docs-utils title: "@kbn/docs-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/docs-utils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils'] --- import kbnDocsUtilsObj from './kbn_docs_utils.devdocs.json'; diff --git a/api_docs/kbn_dom_drag_drop.mdx b/api_docs/kbn_dom_drag_drop.mdx index 0df5f87aaf743..6fdf4e030dd1b 100644 --- a/api_docs/kbn_dom_drag_drop.mdx +++ b/api_docs/kbn_dom_drag_drop.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dom-drag-drop title: "@kbn/dom-drag-drop" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dom-drag-drop plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dom-drag-drop'] --- import kbnDomDragDropObj from './kbn_dom_drag_drop.devdocs.json'; diff --git a/api_docs/kbn_ebt_tools.mdx b/api_docs/kbn_ebt_tools.mdx index 9b3ed90d31062..3c376439a4477 100644 --- a/api_docs/kbn_ebt_tools.mdx +++ b/api_docs/kbn_ebt_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ebt-tools title: "@kbn/ebt-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ebt-tools plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_ecs.mdx b/api_docs/kbn_ecs.mdx index 21ec97781b2b1..09a939e46aa45 100644 --- a/api_docs/kbn_ecs.mdx +++ b/api_docs/kbn_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs title: "@kbn/ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs'] --- import kbnEcsObj from './kbn_ecs.devdocs.json'; diff --git a/api_docs/kbn_ecs_data_quality_dashboard.mdx b/api_docs/kbn_ecs_data_quality_dashboard.mdx index 2c6c0a5d1aab7..9cc7dfefd9678 100644 --- a/api_docs/kbn_ecs_data_quality_dashboard.mdx +++ b/api_docs/kbn_ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs-data-quality-dashboard title: "@kbn/ecs-data-quality-dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs-data-quality-dashboard plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs-data-quality-dashboard'] --- import kbnEcsDataQualityDashboardObj from './kbn_ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/kbn_elastic_assistant.devdocs.json b/api_docs/kbn_elastic_assistant.devdocs.json index b6c7aa46db706..ba770f780e3fd 100644 --- a/api_docs/kbn_elastic_assistant.devdocs.json +++ b/api_docs/kbn_elastic_assistant.devdocs.json @@ -159,7 +159,7 @@ "label": "AssistantProvider", "description": [], "signature": [ - "({ actionTypeRegistry, assistantAvailability, assistantTelemetry, augmentMessageCodeBlocks, baseAllow, baseAllowReplacement, defaultAllow, defaultAllowReplacement, docLinks, basePromptContexts, baseQuickPrompts, baseSystemPrompts, children, getComments, http, getInitialConversations, nameSpace, setConversations, setDefaultAllow, setDefaultAllowReplacement, title, }: React.PropsWithChildren<", + "({ actionTypeRegistry, assistantAvailability, assistantLangChain, assistantTelemetry, augmentMessageCodeBlocks, baseAllow, baseAllowReplacement, defaultAllow, defaultAllowReplacement, docLinks, basePromptContexts, baseQuickPrompts, baseSystemPrompts, children, getComments, http, getInitialConversations, nameSpace, setConversations, setDefaultAllow, setDefaultAllowReplacement, title, }: React.PropsWithChildren<", "AssistantProviderProps", ">) => JSX.Element" ], @@ -172,7 +172,7 @@ "id": "def-public.AssistantProvider.$1", "type": "CompoundType", "tags": [], - "label": "{\n actionTypeRegistry,\n assistantAvailability,\n assistantTelemetry,\n augmentMessageCodeBlocks,\n baseAllow,\n baseAllowReplacement,\n defaultAllow,\n defaultAllowReplacement,\n docLinks,\n basePromptContexts = [],\n baseQuickPrompts = [],\n baseSystemPrompts = BASE_SYSTEM_PROMPTS,\n children,\n getComments,\n http,\n getInitialConversations,\n nameSpace = DEFAULT_ASSISTANT_NAMESPACE,\n setConversations,\n setDefaultAllow,\n setDefaultAllowReplacement,\n title = DEFAULT_ASSISTANT_TITLE,\n}", + "label": "{\n actionTypeRegistry,\n assistantAvailability,\n assistantLangChain,\n assistantTelemetry,\n augmentMessageCodeBlocks,\n baseAllow,\n baseAllowReplacement,\n defaultAllow,\n defaultAllowReplacement,\n docLinks,\n basePromptContexts = [],\n baseQuickPrompts = [],\n baseSystemPrompts = BASE_SYSTEM_PROMPTS,\n children,\n getComments,\n http,\n getInitialConversations,\n nameSpace = DEFAULT_ASSISTANT_NAMESPACE,\n setConversations,\n setDefaultAllow,\n setDefaultAllowReplacement,\n title = DEFAULT_ASSISTANT_TITLE,\n}", "description": [], "signature": [ "React.PropsWithChildren<", diff --git a/api_docs/kbn_elastic_assistant.mdx b/api_docs/kbn_elastic_assistant.mdx index ee2586acf4dfe..700fc56c4a12e 100644 --- a/api_docs/kbn_elastic_assistant.mdx +++ b/api_docs/kbn_elastic_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-assistant title: "@kbn/elastic-assistant" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-assistant plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant'] --- import kbnElasticAssistantObj from './kbn_elastic_assistant.devdocs.json'; diff --git a/api_docs/kbn_es.mdx b/api_docs/kbn_es.mdx index fe4745e9f9d8e..30955056b2f47 100644 --- a/api_docs/kbn_es.mdx +++ b/api_docs/kbn_es.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es title: "@kbn/es" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es'] --- import kbnEsObj from './kbn_es.devdocs.json'; diff --git a/api_docs/kbn_es_archiver.mdx b/api_docs/kbn_es_archiver.mdx index 507b438e96ad9..6be6d9223e19f 100644 --- a/api_docs/kbn_es_archiver.mdx +++ b/api_docs/kbn_es_archiver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-archiver title: "@kbn/es-archiver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-archiver plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-archiver'] --- import kbnEsArchiverObj from './kbn_es_archiver.devdocs.json'; diff --git a/api_docs/kbn_es_errors.mdx b/api_docs/kbn_es_errors.mdx index a57fcae63f6c4..b46623ecdca94 100644 --- a/api_docs/kbn_es_errors.mdx +++ b/api_docs/kbn_es_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-errors title: "@kbn/es-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-errors plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-errors'] --- import kbnEsErrorsObj from './kbn_es_errors.devdocs.json'; diff --git a/api_docs/kbn_es_query.mdx b/api_docs/kbn_es_query.mdx index e021414ada3cf..9eeb9eb239d6f 100644 --- a/api_docs/kbn_es_query.mdx +++ b/api_docs/kbn_es_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-query title: "@kbn/es-query" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-query plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-query'] --- import kbnEsQueryObj from './kbn_es_query.devdocs.json'; diff --git a/api_docs/kbn_es_types.mdx b/api_docs/kbn_es_types.mdx index 3de1ad088b0a8..c02c0e1b6eb54 100644 --- a/api_docs/kbn_es_types.mdx +++ b/api_docs/kbn_es_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-types title: "@kbn/es-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-types plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-types'] --- import kbnEsTypesObj from './kbn_es_types.devdocs.json'; diff --git a/api_docs/kbn_eslint_plugin_imports.mdx b/api_docs/kbn_eslint_plugin_imports.mdx index 17245394d55c5..6632351eb3699 100644 --- a/api_docs/kbn_eslint_plugin_imports.mdx +++ b/api_docs/kbn_eslint_plugin_imports.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-eslint-plugin-imports title: "@kbn/eslint-plugin-imports" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/eslint-plugin-imports plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_event_annotation_common.mdx b/api_docs/kbn_event_annotation_common.mdx index ba4a96a999de7..8b6adb20854b8 100644 --- a/api_docs/kbn_event_annotation_common.mdx +++ b/api_docs/kbn_event_annotation_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-common title: "@kbn/event-annotation-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-common plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/event-annotation-common'] --- import kbnEventAnnotationCommonObj from './kbn_event_annotation_common.devdocs.json'; diff --git a/api_docs/kbn_event_annotation_components.mdx b/api_docs/kbn_event_annotation_components.mdx index 9c3cbb50d3609..acf47db88e44a 100644 --- a/api_docs/kbn_event_annotation_components.mdx +++ b/api_docs/kbn_event_annotation_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-components title: "@kbn/event-annotation-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-components plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/event-annotation-components'] --- import kbnEventAnnotationComponentsObj from './kbn_event_annotation_components.devdocs.json'; diff --git a/api_docs/kbn_expandable_flyout.mdx b/api_docs/kbn_expandable_flyout.mdx index 7ce3c1812c692..f6d5e017e5ece 100644 --- a/api_docs/kbn_expandable_flyout.mdx +++ b/api_docs/kbn_expandable_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-expandable-flyout title: "@kbn/expandable-flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/expandable-flyout plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/expandable-flyout'] --- import kbnExpandableFlyoutObj from './kbn_expandable_flyout.devdocs.json'; diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index 38c5d06324e16..f3c5be9d72749 100644 --- a/api_docs/kbn_field_types.mdx +++ b/api_docs/kbn_field_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-types title: "@kbn/field-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-types plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types'] --- import kbnFieldTypesObj from './kbn_field_types.devdocs.json'; diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx index 8f4bc0685a40f..9b304e48ad970 100644 --- a/api_docs/kbn_find_used_node_modules.mdx +++ b/api_docs/kbn_find_used_node_modules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-find-used-node-modules title: "@kbn/find-used-node-modules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/find-used-node-modules plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/find-used-node-modules'] --- import kbnFindUsedNodeModulesObj from './kbn_find_used_node_modules.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_services.mdx b/api_docs/kbn_ftr_common_functional_services.mdx index b553a82db6376..6366a800e19ce 100644 --- a/api_docs/kbn_ftr_common_functional_services.mdx +++ b/api_docs/kbn_ftr_common_functional_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-services title: "@kbn/ftr-common-functional-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-services plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-services'] --- import kbnFtrCommonFunctionalServicesObj from './kbn_ftr_common_functional_services.devdocs.json'; diff --git a/api_docs/kbn_generate.mdx b/api_docs/kbn_generate.mdx index e0c1caf46cdd6..d9df69c0e3591 100644 --- a/api_docs/kbn_generate.mdx +++ b/api_docs/kbn_generate.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate title: "@kbn/generate" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_generate_console_definitions.mdx b/api_docs/kbn_generate_console_definitions.mdx index b569181134c02..4a9a08964a21b 100644 --- a/api_docs/kbn_generate_console_definitions.mdx +++ b/api_docs/kbn_generate_console_definitions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-console-definitions title: "@kbn/generate-console-definitions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-console-definitions plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-console-definitions'] --- import kbnGenerateConsoleDefinitionsObj from './kbn_generate_console_definitions.devdocs.json'; diff --git a/api_docs/kbn_generate_csv.mdx b/api_docs/kbn_generate_csv.mdx index e943f85bc60a7..edb0ef008da43 100644 --- a/api_docs/kbn_generate_csv.mdx +++ b/api_docs/kbn_generate_csv.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-csv title: "@kbn/generate-csv" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-csv plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv'] --- import kbnGenerateCsvObj from './kbn_generate_csv.devdocs.json'; diff --git a/api_docs/kbn_generate_csv_types.mdx b/api_docs/kbn_generate_csv_types.mdx index d631d48ef8c10..1477071967337 100644 --- a/api_docs/kbn_generate_csv_types.mdx +++ b/api_docs/kbn_generate_csv_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-csv-types title: "@kbn/generate-csv-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-csv-types plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv-types'] --- import kbnGenerateCsvTypesObj from './kbn_generate_csv_types.devdocs.json'; diff --git a/api_docs/kbn_guided_onboarding.mdx b/api_docs/kbn_guided_onboarding.mdx index 745ac02bb4652..8ac44ad4ac395 100644 --- a/api_docs/kbn_guided_onboarding.mdx +++ b/api_docs/kbn_guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-guided-onboarding title: "@kbn/guided-onboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/guided-onboarding plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/guided-onboarding'] --- import kbnGuidedOnboardingObj from './kbn_guided_onboarding.devdocs.json'; diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index ca5da2e9ab6c6..b5c9b0a4c7905 100644 --- a/api_docs/kbn_handlebars.mdx +++ b/api_docs/kbn_handlebars.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-handlebars title: "@kbn/handlebars" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/handlebars plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/handlebars'] --- import kbnHandlebarsObj from './kbn_handlebars.devdocs.json'; diff --git a/api_docs/kbn_hapi_mocks.mdx b/api_docs/kbn_hapi_mocks.mdx index cff27e0e07eca..8c5ee973427fa 100644 --- a/api_docs/kbn_hapi_mocks.mdx +++ b/api_docs/kbn_hapi_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-hapi-mocks title: "@kbn/hapi-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/hapi-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/hapi-mocks'] --- import kbnHapiMocksObj from './kbn_hapi_mocks.devdocs.json'; diff --git a/api_docs/kbn_health_gateway_server.mdx b/api_docs/kbn_health_gateway_server.mdx index 322e7222fc25a..2d5301b748a32 100644 --- a/api_docs/kbn_health_gateway_server.mdx +++ b/api_docs/kbn_health_gateway_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-health-gateway-server title: "@kbn/health-gateway-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/health-gateway-server plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/health-gateway-server'] --- import kbnHealthGatewayServerObj from './kbn_health_gateway_server.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_card.mdx b/api_docs/kbn_home_sample_data_card.mdx index 77b545e3c2a94..0d2035b575cf9 100644 --- a/api_docs/kbn_home_sample_data_card.mdx +++ b/api_docs/kbn_home_sample_data_card.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-card title: "@kbn/home-sample-data-card" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-card plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-card'] --- import kbnHomeSampleDataCardObj from './kbn_home_sample_data_card.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_tab.mdx b/api_docs/kbn_home_sample_data_tab.mdx index a8ef4c315ee91..aa44a7339f2e1 100644 --- a/api_docs/kbn_home_sample_data_tab.mdx +++ b/api_docs/kbn_home_sample_data_tab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-tab title: "@kbn/home-sample-data-tab" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-tab plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-tab'] --- import kbnHomeSampleDataTabObj from './kbn_home_sample_data_tab.devdocs.json'; diff --git a/api_docs/kbn_i18n.mdx b/api_docs/kbn_i18n.mdx index f1c2c07026250..37af28638ba5f 100644 --- a/api_docs/kbn_i18n.mdx +++ b/api_docs/kbn_i18n.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n title: "@kbn/i18n" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n'] --- import kbnI18nObj from './kbn_i18n.devdocs.json'; diff --git a/api_docs/kbn_i18n_react.mdx b/api_docs/kbn_i18n_react.mdx index 4de3e62ab8222..1c4e9645f6cb8 100644 --- a/api_docs/kbn_i18n_react.mdx +++ b/api_docs/kbn_i18n_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n-react title: "@kbn/i18n-react" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n-react plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n-react'] --- import kbnI18nReactObj from './kbn_i18n_react.devdocs.json'; diff --git a/api_docs/kbn_import_resolver.mdx b/api_docs/kbn_import_resolver.mdx index 4a2fb1374ce93..d7d4ec86c975e 100644 --- a/api_docs/kbn_import_resolver.mdx +++ b/api_docs/kbn_import_resolver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-import-resolver title: "@kbn/import-resolver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/import-resolver plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_infra_forge.mdx b/api_docs/kbn_infra_forge.mdx index c9c3a03be7dd8..141e3bd5f5893 100644 --- a/api_docs/kbn_infra_forge.mdx +++ b/api_docs/kbn_infra_forge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-infra-forge title: "@kbn/infra-forge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/infra-forge plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/infra-forge'] --- import kbnInfraForgeObj from './kbn_infra_forge.devdocs.json'; diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index 6eb1de41649fb..fc2149869e4ad 100644 --- a/api_docs/kbn_interpreter.mdx +++ b/api_docs/kbn_interpreter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-interpreter title: "@kbn/interpreter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/interpreter plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/interpreter'] --- import kbnInterpreterObj from './kbn_interpreter.devdocs.json'; diff --git a/api_docs/kbn_io_ts_utils.mdx b/api_docs/kbn_io_ts_utils.mdx index 5eacd3894b8a9..2ef5fad37ec2f 100644 --- a/api_docs/kbn_io_ts_utils.mdx +++ b/api_docs/kbn_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-io-ts-utils title: "@kbn/io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/io-ts-utils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] --- import kbnIoTsUtilsObj from './kbn_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index b655478312cee..17eb9593f193a 100644 --- a/api_docs/kbn_jest_serializers.mdx +++ b/api_docs/kbn_jest_serializers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-jest-serializers title: "@kbn/jest-serializers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/jest-serializers plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/jest-serializers'] --- import kbnJestSerializersObj from './kbn_jest_serializers.devdocs.json'; diff --git a/api_docs/kbn_journeys.mdx b/api_docs/kbn_journeys.mdx index 304eedf004302..298d1c91f18ca 100644 --- a/api_docs/kbn_journeys.mdx +++ b/api_docs/kbn_journeys.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-journeys title: "@kbn/journeys" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/journeys plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/journeys'] --- import kbnJourneysObj from './kbn_journeys.devdocs.json'; diff --git a/api_docs/kbn_json_ast.mdx b/api_docs/kbn_json_ast.mdx index bbd52d0396413..f3f26735d6d43 100644 --- a/api_docs/kbn_json_ast.mdx +++ b/api_docs/kbn_json_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-json-ast title: "@kbn/json-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/json-ast plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/json-ast'] --- import kbnJsonAstObj from './kbn_json_ast.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_schema.mdx b/api_docs/kbn_kibana_manifest_schema.mdx index 26fa24d84da0e..50dc441905e96 100644 --- a/api_docs/kbn_kibana_manifest_schema.mdx +++ b/api_docs/kbn_kibana_manifest_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-kibana-manifest-schema title: "@kbn/kibana-manifest-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/kibana-manifest-schema plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-schema'] --- import kbnKibanaManifestSchemaObj from './kbn_kibana_manifest_schema.devdocs.json'; diff --git a/api_docs/kbn_language_documentation_popover.mdx b/api_docs/kbn_language_documentation_popover.mdx index cc28ae74312fe..de335a8f1d70d 100644 --- a/api_docs/kbn_language_documentation_popover.mdx +++ b/api_docs/kbn_language_documentation_popover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-language-documentation-popover title: "@kbn/language-documentation-popover" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/language-documentation-popover plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/language-documentation-popover'] --- import kbnLanguageDocumentationPopoverObj from './kbn_language_documentation_popover.devdocs.json'; diff --git a/api_docs/kbn_lens_embeddable_utils.mdx b/api_docs/kbn_lens_embeddable_utils.mdx index 491b75fe5c513..c3d3f1dfab9f6 100644 --- a/api_docs/kbn_lens_embeddable_utils.mdx +++ b/api_docs/kbn_lens_embeddable_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-lens-embeddable-utils title: "@kbn/lens-embeddable-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/lens-embeddable-utils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/lens-embeddable-utils'] --- import kbnLensEmbeddableUtilsObj from './kbn_lens_embeddable_utils.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index 43d53e563567f..e10f9f15513ad 100644 --- a/api_docs/kbn_logging.mdx +++ b/api_docs/kbn_logging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging title: "@kbn/logging" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging'] --- import kbnLoggingObj from './kbn_logging.devdocs.json'; diff --git a/api_docs/kbn_logging_mocks.mdx b/api_docs/kbn_logging_mocks.mdx index 824fcf105da85..9659851be9414 100644 --- a/api_docs/kbn_logging_mocks.mdx +++ b/api_docs/kbn_logging_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging-mocks title: "@kbn/logging-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] --- import kbnLoggingMocksObj from './kbn_logging_mocks.devdocs.json'; diff --git a/api_docs/kbn_managed_vscode_config.mdx b/api_docs/kbn_managed_vscode_config.mdx index c3f0b20b0982f..18ed8a93add58 100644 --- a/api_docs/kbn_managed_vscode_config.mdx +++ b/api_docs/kbn_managed_vscode_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-vscode-config title: "@kbn/managed-vscode-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-vscode-config plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-vscode-config'] --- import kbnManagedVscodeConfigObj from './kbn_managed_vscode_config.devdocs.json'; diff --git a/api_docs/kbn_management_cards_navigation.mdx b/api_docs/kbn_management_cards_navigation.mdx index 574195bbcf6ba..d4c7f326d8abd 100644 --- a/api_docs/kbn_management_cards_navigation.mdx +++ b/api_docs/kbn_management_cards_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-cards-navigation title: "@kbn/management-cards-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-cards-navigation plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-cards-navigation'] --- import kbnManagementCardsNavigationObj from './kbn_management_cards_navigation.devdocs.json'; diff --git a/api_docs/kbn_management_settings_section_registry.mdx b/api_docs/kbn_management_settings_section_registry.mdx index 3765faadee51d..072feb8e3d7fc 100644 --- a/api_docs/kbn_management_settings_section_registry.mdx +++ b/api_docs/kbn_management_settings_section_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-section-registry title: "@kbn/management-settings-section-registry" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-section-registry plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-section-registry'] --- import kbnManagementSettingsSectionRegistryObj from './kbn_management_settings_section_registry.devdocs.json'; diff --git a/api_docs/kbn_management_storybook_config.mdx b/api_docs/kbn_management_storybook_config.mdx index 58a3936fcc4a6..df5b2b135f70f 100644 --- a/api_docs/kbn_management_storybook_config.mdx +++ b/api_docs/kbn_management_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-storybook-config title: "@kbn/management-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-storybook-config plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-storybook-config'] --- import kbnManagementStorybookConfigObj from './kbn_management_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index 915b3bd624819..c27e4ee9909f3 100644 --- a/api_docs/kbn_mapbox_gl.mdx +++ b/api_docs/kbn_mapbox_gl.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mapbox-gl title: "@kbn/mapbox-gl" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mapbox-gl plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] --- import kbnMapboxGlObj from './kbn_mapbox_gl.devdocs.json'; diff --git a/api_docs/kbn_maps_vector_tile_utils.mdx b/api_docs/kbn_maps_vector_tile_utils.mdx index 9173d46071a59..752deb601de6b 100644 --- a/api_docs/kbn_maps_vector_tile_utils.mdx +++ b/api_docs/kbn_maps_vector_tile_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-maps-vector-tile-utils title: "@kbn/maps-vector-tile-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/maps-vector-tile-utils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/maps-vector-tile-utils'] --- import kbnMapsVectorTileUtilsObj from './kbn_maps_vector_tile_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_agg_utils.mdx b/api_docs/kbn_ml_agg_utils.mdx index 37cd08fc67a22..c72e0421d1744 100644 --- a/api_docs/kbn_ml_agg_utils.mdx +++ b/api_docs/kbn_ml_agg_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-agg-utils title: "@kbn/ml-agg-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-agg-utils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-agg-utils'] --- import kbnMlAggUtilsObj from './kbn_ml_agg_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_anomaly_utils.mdx b/api_docs/kbn_ml_anomaly_utils.mdx index 5e9e578ae8dd5..0310d80f4f4ee 100644 --- a/api_docs/kbn_ml_anomaly_utils.mdx +++ b/api_docs/kbn_ml_anomaly_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-anomaly-utils title: "@kbn/ml-anomaly-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-anomaly-utils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-anomaly-utils'] --- import kbnMlAnomalyUtilsObj from './kbn_ml_anomaly_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_category_validator.mdx b/api_docs/kbn_ml_category_validator.mdx index aec6235823d5c..12cea1e4fc52d 100644 --- a/api_docs/kbn_ml_category_validator.mdx +++ b/api_docs/kbn_ml_category_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-category-validator title: "@kbn/ml-category-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-category-validator plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-category-validator'] --- import kbnMlCategoryValidatorObj from './kbn_ml_category_validator.devdocs.json'; diff --git a/api_docs/kbn_ml_data_frame_analytics_utils.mdx b/api_docs/kbn_ml_data_frame_analytics_utils.mdx index bf951e4a23cfa..e08dcf67b5b77 100644 --- a/api_docs/kbn_ml_data_frame_analytics_utils.mdx +++ b/api_docs/kbn_ml_data_frame_analytics_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-frame-analytics-utils title: "@kbn/ml-data-frame-analytics-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-frame-analytics-utils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-data-frame-analytics-utils'] --- import kbnMlDataFrameAnalyticsUtilsObj from './kbn_ml_data_frame_analytics_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_data_grid.mdx b/api_docs/kbn_ml_data_grid.mdx index a7b292c5e0d19..7e7eb6c824698 100644 --- a/api_docs/kbn_ml_data_grid.mdx +++ b/api_docs/kbn_ml_data_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-grid title: "@kbn/ml-data-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-grid plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-data-grid'] --- import kbnMlDataGridObj from './kbn_ml_data_grid.devdocs.json'; diff --git a/api_docs/kbn_ml_date_picker.mdx b/api_docs/kbn_ml_date_picker.mdx index 7fb13246ad50d..22be4828a09b4 100644 --- a/api_docs/kbn_ml_date_picker.mdx +++ b/api_docs/kbn_ml_date_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-picker title: "@kbn/ml-date-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-picker plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-picker'] --- import kbnMlDatePickerObj from './kbn_ml_date_picker.devdocs.json'; diff --git a/api_docs/kbn_ml_date_utils.mdx b/api_docs/kbn_ml_date_utils.mdx index 54afbaf8bb4c6..2cddfbacbe995 100644 --- a/api_docs/kbn_ml_date_utils.mdx +++ b/api_docs/kbn_ml_date_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-utils title: "@kbn/ml-date-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-utils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-utils'] --- import kbnMlDateUtilsObj from './kbn_ml_date_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_error_utils.mdx b/api_docs/kbn_ml_error_utils.mdx index 3abc33688e974..115d7f3f5aebb 100644 --- a/api_docs/kbn_ml_error_utils.mdx +++ b/api_docs/kbn_ml_error_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-error-utils title: "@kbn/ml-error-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-error-utils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-error-utils'] --- import kbnMlErrorUtilsObj from './kbn_ml_error_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_in_memory_table.mdx b/api_docs/kbn_ml_in_memory_table.mdx index 43c8dccc8f09b..bce3c119c7300 100644 --- a/api_docs/kbn_ml_in_memory_table.mdx +++ b/api_docs/kbn_ml_in_memory_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-in-memory-table title: "@kbn/ml-in-memory-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-in-memory-table plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-in-memory-table'] --- import kbnMlInMemoryTableObj from './kbn_ml_in_memory_table.devdocs.json'; diff --git a/api_docs/kbn_ml_is_defined.mdx b/api_docs/kbn_ml_is_defined.mdx index fdf47821ef58a..10e9a37cf2a1d 100644 --- a/api_docs/kbn_ml_is_defined.mdx +++ b/api_docs/kbn_ml_is_defined.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-defined title: "@kbn/ml-is-defined" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-defined plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-defined'] --- import kbnMlIsDefinedObj from './kbn_ml_is_defined.devdocs.json'; diff --git a/api_docs/kbn_ml_is_populated_object.mdx b/api_docs/kbn_ml_is_populated_object.mdx index 90c913bf11ddb..c84cd32d693e5 100644 --- a/api_docs/kbn_ml_is_populated_object.mdx +++ b/api_docs/kbn_ml_is_populated_object.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-populated-object title: "@kbn/ml-is-populated-object" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-populated-object plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-populated-object'] --- import kbnMlIsPopulatedObjectObj from './kbn_ml_is_populated_object.devdocs.json'; diff --git a/api_docs/kbn_ml_kibana_theme.mdx b/api_docs/kbn_ml_kibana_theme.mdx index fb3edc3daf05b..b6632ced1864f 100644 --- a/api_docs/kbn_ml_kibana_theme.mdx +++ b/api_docs/kbn_ml_kibana_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-kibana-theme title: "@kbn/ml-kibana-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-kibana-theme plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-kibana-theme'] --- import kbnMlKibanaThemeObj from './kbn_ml_kibana_theme.devdocs.json'; diff --git a/api_docs/kbn_ml_local_storage.mdx b/api_docs/kbn_ml_local_storage.mdx index cfc725540ece3..d2aa2d2e11369 100644 --- a/api_docs/kbn_ml_local_storage.mdx +++ b/api_docs/kbn_ml_local_storage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-local-storage title: "@kbn/ml-local-storage" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-local-storage plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-local-storage'] --- import kbnMlLocalStorageObj from './kbn_ml_local_storage.devdocs.json'; diff --git a/api_docs/kbn_ml_nested_property.mdx b/api_docs/kbn_ml_nested_property.mdx index b5f9d79cf2049..865cb28ba68d7 100644 --- a/api_docs/kbn_ml_nested_property.mdx +++ b/api_docs/kbn_ml_nested_property.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-nested-property title: "@kbn/ml-nested-property" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-nested-property plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-nested-property'] --- import kbnMlNestedPropertyObj from './kbn_ml_nested_property.devdocs.json'; diff --git a/api_docs/kbn_ml_number_utils.mdx b/api_docs/kbn_ml_number_utils.mdx index e36cc3817f56a..0c7c2d69773df 100644 --- a/api_docs/kbn_ml_number_utils.mdx +++ b/api_docs/kbn_ml_number_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-number-utils title: "@kbn/ml-number-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-number-utils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-number-utils'] --- import kbnMlNumberUtilsObj from './kbn_ml_number_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_query_utils.mdx b/api_docs/kbn_ml_query_utils.mdx index 2aabe72429f12..c8b5a1f564921 100644 --- a/api_docs/kbn_ml_query_utils.mdx +++ b/api_docs/kbn_ml_query_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-query-utils title: "@kbn/ml-query-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-query-utils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-query-utils'] --- import kbnMlQueryUtilsObj from './kbn_ml_query_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_random_sampler_utils.mdx b/api_docs/kbn_ml_random_sampler_utils.mdx index c98fd1e08923f..d88fa51624c10 100644 --- a/api_docs/kbn_ml_random_sampler_utils.mdx +++ b/api_docs/kbn_ml_random_sampler_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-random-sampler-utils title: "@kbn/ml-random-sampler-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-random-sampler-utils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-random-sampler-utils'] --- import kbnMlRandomSamplerUtilsObj from './kbn_ml_random_sampler_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_route_utils.mdx b/api_docs/kbn_ml_route_utils.mdx index bf752bf195060..334b5d977b445 100644 --- a/api_docs/kbn_ml_route_utils.mdx +++ b/api_docs/kbn_ml_route_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-route-utils title: "@kbn/ml-route-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-route-utils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-route-utils'] --- import kbnMlRouteUtilsObj from './kbn_ml_route_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_runtime_field_utils.mdx b/api_docs/kbn_ml_runtime_field_utils.mdx index f5ee0d61ba730..56bb547e1b02d 100644 --- a/api_docs/kbn_ml_runtime_field_utils.mdx +++ b/api_docs/kbn_ml_runtime_field_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-runtime-field-utils title: "@kbn/ml-runtime-field-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-runtime-field-utils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-runtime-field-utils'] --- import kbnMlRuntimeFieldUtilsObj from './kbn_ml_runtime_field_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index a1aa76891a753..7f7db30ddc077 100644 --- a/api_docs/kbn_ml_string_hash.mdx +++ b/api_docs/kbn_ml_string_hash.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-string-hash title: "@kbn/ml-string-hash" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-string-hash plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_ml_trained_models_utils.mdx b/api_docs/kbn_ml_trained_models_utils.mdx index 0b464612f81a1..fa07e095326b3 100644 --- a/api_docs/kbn_ml_trained_models_utils.mdx +++ b/api_docs/kbn_ml_trained_models_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-trained-models-utils title: "@kbn/ml-trained-models-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-trained-models-utils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-trained-models-utils'] --- import kbnMlTrainedModelsUtilsObj from './kbn_ml_trained_models_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_url_state.mdx b/api_docs/kbn_ml_url_state.mdx index 0ecd9bb6ea36f..a829a05d9e99f 100644 --- a/api_docs/kbn_ml_url_state.mdx +++ b/api_docs/kbn_ml_url_state.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-url-state title: "@kbn/ml-url-state" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-url-state plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-url-state'] --- import kbnMlUrlStateObj from './kbn_ml_url_state.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index d1d7e3632b39c..f2561211fbddb 100644 --- a/api_docs/kbn_monaco.mdx +++ b/api_docs/kbn_monaco.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-monaco title: "@kbn/monaco" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/monaco plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.devdocs.json'; diff --git a/api_docs/kbn_object_versioning.mdx b/api_docs/kbn_object_versioning.mdx index b26b50f40ed01..cee70296ebec6 100644 --- a/api_docs/kbn_object_versioning.mdx +++ b/api_docs/kbn_object_versioning.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-object-versioning title: "@kbn/object-versioning" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/object-versioning plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/object-versioning'] --- import kbnObjectVersioningObj from './kbn_object_versioning.devdocs.json'; diff --git a/api_docs/kbn_observability_alert_details.mdx b/api_docs/kbn_observability_alert_details.mdx index a750f3434012f..8276e0f455d6d 100644 --- a/api_docs/kbn_observability_alert_details.mdx +++ b/api_docs/kbn_observability_alert_details.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alert-details title: "@kbn/observability-alert-details" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alert-details plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alert-details'] --- import kbnObservabilityAlertDetailsObj from './kbn_observability_alert_details.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index 3a15e1a51fd61..5a55501f9467a 100644 --- a/api_docs/kbn_optimizer.mdx +++ b/api_docs/kbn_optimizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer title: "@kbn/optimizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer'] --- import kbnOptimizerObj from './kbn_optimizer.devdocs.json'; diff --git a/api_docs/kbn_optimizer_webpack_helpers.mdx b/api_docs/kbn_optimizer_webpack_helpers.mdx index b01b8c1f65cb1..354fbcba634ad 100644 --- a/api_docs/kbn_optimizer_webpack_helpers.mdx +++ b/api_docs/kbn_optimizer_webpack_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer-webpack-helpers title: "@kbn/optimizer-webpack-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer-webpack-helpers plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer-webpack-helpers'] --- import kbnOptimizerWebpackHelpersObj from './kbn_optimizer_webpack_helpers.devdocs.json'; diff --git a/api_docs/kbn_osquery_io_ts_types.mdx b/api_docs/kbn_osquery_io_ts_types.mdx index 5a32ff29ce1f5..b163df01ae225 100644 --- a/api_docs/kbn_osquery_io_ts_types.mdx +++ b/api_docs/kbn_osquery_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-osquery-io-ts-types title: "@kbn/osquery-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/osquery-io-ts-types plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/osquery-io-ts-types'] --- import kbnOsqueryIoTsTypesObj from './kbn_osquery_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index f983a944135a5..a11c97784e509 100644 --- a/api_docs/kbn_performance_testing_dataset_extractor.mdx +++ b/api_docs/kbn_performance_testing_dataset_extractor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-performance-testing-dataset-extractor title: "@kbn/performance-testing-dataset-extractor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/performance-testing-dataset-extractor plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/performance-testing-dataset-extractor'] --- import kbnPerformanceTestingDatasetExtractorObj from './kbn_performance_testing_dataset_extractor.devdocs.json'; diff --git a/api_docs/kbn_plugin_generator.mdx b/api_docs/kbn_plugin_generator.mdx index fcb8f3f01c4f6..a94e7eb5102da 100644 --- a/api_docs/kbn_plugin_generator.mdx +++ b/api_docs/kbn_plugin_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-generator title: "@kbn/plugin-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-generator plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-generator'] --- import kbnPluginGeneratorObj from './kbn_plugin_generator.devdocs.json'; diff --git a/api_docs/kbn_plugin_helpers.mdx b/api_docs/kbn_plugin_helpers.mdx index 1faca1531ed9c..3a1b036ca3013 100644 --- a/api_docs/kbn_plugin_helpers.mdx +++ b/api_docs/kbn_plugin_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-helpers title: "@kbn/plugin-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-helpers plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_random_sampling.mdx b/api_docs/kbn_random_sampling.mdx index 3b37e1e56935f..a8949b64310b9 100644 --- a/api_docs/kbn_random_sampling.mdx +++ b/api_docs/kbn_random_sampling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-random-sampling title: "@kbn/random-sampling" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/random-sampling plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/random-sampling'] --- import kbnRandomSamplingObj from './kbn_random_sampling.devdocs.json'; diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index 04d218646a71d..7c712164fd8cb 100644 --- a/api_docs/kbn_react_field.mdx +++ b/api_docs/kbn_react_field.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-field title: "@kbn/react-field" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-field plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_common.mdx b/api_docs/kbn_react_kibana_context_common.mdx index 6dbcb3c0ff5c8..2f2893afffa81 100644 --- a/api_docs/kbn_react_kibana_context_common.mdx +++ b/api_docs/kbn_react_kibana_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-common title: "@kbn/react-kibana-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-common plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-common'] --- import kbnReactKibanaContextCommonObj from './kbn_react_kibana_context_common.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_render.mdx b/api_docs/kbn_react_kibana_context_render.mdx index 0807075b7caf7..3871fd28dfb72 100644 --- a/api_docs/kbn_react_kibana_context_render.mdx +++ b/api_docs/kbn_react_kibana_context_render.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-render title: "@kbn/react-kibana-context-render" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-render plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-render'] --- import kbnReactKibanaContextRenderObj from './kbn_react_kibana_context_render.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_root.mdx b/api_docs/kbn_react_kibana_context_root.mdx index 6bf44b4f5cb95..a0e9eafcf0b21 100644 --- a/api_docs/kbn_react_kibana_context_root.mdx +++ b/api_docs/kbn_react_kibana_context_root.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-root title: "@kbn/react-kibana-context-root" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-root plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-root'] --- import kbnReactKibanaContextRootObj from './kbn_react_kibana_context_root.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_styled.mdx b/api_docs/kbn_react_kibana_context_styled.mdx index cdaac4b551dee..cca223f50e6a0 100644 --- a/api_docs/kbn_react_kibana_context_styled.mdx +++ b/api_docs/kbn_react_kibana_context_styled.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-styled title: "@kbn/react-kibana-context-styled" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-styled plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-styled'] --- import kbnReactKibanaContextStyledObj from './kbn_react_kibana_context_styled.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_theme.mdx b/api_docs/kbn_react_kibana_context_theme.mdx index 202095f9413f5..e3281ee6b5352 100644 --- a/api_docs/kbn_react_kibana_context_theme.mdx +++ b/api_docs/kbn_react_kibana_context_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-theme title: "@kbn/react-kibana-context-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-theme plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-theme'] --- import kbnReactKibanaContextThemeObj from './kbn_react_kibana_context_theme.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_mount.mdx b/api_docs/kbn_react_kibana_mount.mdx index c2e3b7228c3aa..299d5f22b976d 100644 --- a/api_docs/kbn_react_kibana_mount.mdx +++ b/api_docs/kbn_react_kibana_mount.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-mount title: "@kbn/react-kibana-mount" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-mount plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-mount'] --- import kbnReactKibanaMountObj from './kbn_react_kibana_mount.devdocs.json'; diff --git a/api_docs/kbn_repo_file_maps.mdx b/api_docs/kbn_repo_file_maps.mdx index 0cf797ebc817b..ce8158a8183c7 100644 --- a/api_docs/kbn_repo_file_maps.mdx +++ b/api_docs/kbn_repo_file_maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-file-maps title: "@kbn/repo-file-maps" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-file-maps plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-file-maps'] --- import kbnRepoFileMapsObj from './kbn_repo_file_maps.devdocs.json'; diff --git a/api_docs/kbn_repo_linter.mdx b/api_docs/kbn_repo_linter.mdx index 8795a2ff87d2b..87efc1001f627 100644 --- a/api_docs/kbn_repo_linter.mdx +++ b/api_docs/kbn_repo_linter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-linter title: "@kbn/repo-linter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-linter plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-linter'] --- import kbnRepoLinterObj from './kbn_repo_linter.devdocs.json'; diff --git a/api_docs/kbn_repo_path.mdx b/api_docs/kbn_repo_path.mdx index ba63caf870dc3..ca4a6449f390c 100644 --- a/api_docs/kbn_repo_path.mdx +++ b/api_docs/kbn_repo_path.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-path title: "@kbn/repo-path" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-path plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-path'] --- import kbnRepoPathObj from './kbn_repo_path.devdocs.json'; diff --git a/api_docs/kbn_repo_source_classifier.mdx b/api_docs/kbn_repo_source_classifier.mdx index c24c5aceb94d6..2e8a262dd0d34 100644 --- a/api_docs/kbn_repo_source_classifier.mdx +++ b/api_docs/kbn_repo_source_classifier.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-source-classifier title: "@kbn/repo-source-classifier" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-source-classifier plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-source-classifier'] --- import kbnRepoSourceClassifierObj from './kbn_repo_source_classifier.devdocs.json'; diff --git a/api_docs/kbn_reporting_common.mdx b/api_docs/kbn_reporting_common.mdx index 980fd1a39d79a..9d66b484597d9 100644 --- a/api_docs/kbn_reporting_common.mdx +++ b/api_docs/kbn_reporting_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-common title: "@kbn/reporting-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-common plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-common'] --- import kbnReportingCommonObj from './kbn_reporting_common.devdocs.json'; diff --git a/api_docs/kbn_rison.mdx b/api_docs/kbn_rison.mdx index 9a1902668d8d8..5c01b7a0b78c6 100644 --- a/api_docs/kbn_rison.mdx +++ b/api_docs/kbn_rison.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rison title: "@kbn/rison" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rison plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rison'] --- import kbnRisonObj from './kbn_rison.devdocs.json'; diff --git a/api_docs/kbn_rrule.mdx b/api_docs/kbn_rrule.mdx index f2fefc4c97da3..cbc74d84905af 100644 --- a/api_docs/kbn_rrule.mdx +++ b/api_docs/kbn_rrule.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rrule title: "@kbn/rrule" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rrule plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rrule'] --- import kbnRruleObj from './kbn_rrule.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index 50cffdb0363ce..d5938991671d2 100644 --- a/api_docs/kbn_rule_data_utils.mdx +++ b/api_docs/kbn_rule_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rule-data-utils title: "@kbn/rule-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rule-data-utils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] --- import kbnRuleDataUtilsObj from './kbn_rule_data_utils.devdocs.json'; diff --git a/api_docs/kbn_saved_objects_settings.mdx b/api_docs/kbn_saved_objects_settings.mdx index e871523c539c7..f82e8f71c8431 100644 --- a/api_docs/kbn_saved_objects_settings.mdx +++ b/api_docs/kbn_saved_objects_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-saved-objects-settings title: "@kbn/saved-objects-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/saved-objects-settings plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/saved-objects-settings'] --- import kbnSavedObjectsSettingsObj from './kbn_saved_objects_settings.devdocs.json'; diff --git a/api_docs/kbn_search_api_panels.devdocs.json b/api_docs/kbn_search_api_panels.devdocs.json index d477957673f4f..10d786f99db67 100644 --- a/api_docs/kbn_search_api_panels.devdocs.json +++ b/api_docs/kbn_search_api_panels.devdocs.json @@ -583,12 +583,12 @@ { "parentPluginId": "@kbn/search-api-panels", "id": "def-common.IntegrationsPanelProps.docLinks", - "type": "Any", + "type": "Object", "tags": [], "label": "docLinks", "description": [], "signature": [ - "any" + "{ beats: string; connectors: string; logStash: string; }" ], "path": "packages/kbn-search-api-panels/components/integrations_panel.tsx", "deprecated": false, diff --git a/api_docs/kbn_search_api_panels.mdx b/api_docs/kbn_search_api_panels.mdx index a9d0d9883300f..51ae6d16ef47f 100644 --- a/api_docs/kbn_search_api_panels.mdx +++ b/api_docs/kbn_search_api_panels.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-api-panels title: "@kbn/search-api-panels" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-api-panels plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-api-panels'] --- import kbnSearchApiPanelsObj from './kbn_search_api_panels.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/te | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 64 | 1 | 64 | 0 | +| 64 | 0 | 64 | 0 | ## Common diff --git a/api_docs/kbn_search_response_warnings.mdx b/api_docs/kbn_search_response_warnings.mdx index 8dc0aec607d68..d658e52122353 100644 --- a/api_docs/kbn_search_response_warnings.mdx +++ b/api_docs/kbn_search_response_warnings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-response-warnings title: "@kbn/search-response-warnings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-response-warnings plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-response-warnings'] --- import kbnSearchResponseWarningsObj from './kbn_search_response_warnings.devdocs.json'; diff --git a/api_docs/kbn_security_solution_navigation.mdx b/api_docs/kbn_security_solution_navigation.mdx index 3a92db9b0f639..c487ad465c6d7 100644 --- a/api_docs/kbn_security_solution_navigation.mdx +++ b/api_docs/kbn_security_solution_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-navigation title: "@kbn/security-solution-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-navigation plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-navigation'] --- import kbnSecuritySolutionNavigationObj from './kbn_security_solution_navigation.devdocs.json'; diff --git a/api_docs/kbn_security_solution_side_nav.mdx b/api_docs/kbn_security_solution_side_nav.mdx index 95a0b834d2639..2afb39253c2c8 100644 --- a/api_docs/kbn_security_solution_side_nav.mdx +++ b/api_docs/kbn_security_solution_side_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-side-nav title: "@kbn/security-solution-side-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-side-nav plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-side-nav'] --- import kbnSecuritySolutionSideNavObj from './kbn_security_solution_side_nav.devdocs.json'; diff --git a/api_docs/kbn_security_solution_storybook_config.mdx b/api_docs/kbn_security_solution_storybook_config.mdx index 77c3f6a7c1f55..f4d968a9b8ae7 100644 --- a/api_docs/kbn_security_solution_storybook_config.mdx +++ b/api_docs/kbn_security_solution_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-storybook-config title: "@kbn/security-solution-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-storybook-config plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-storybook-config'] --- import kbnSecuritySolutionStorybookConfigObj from './kbn_security_solution_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index b30e35ef27ac7..160e4b33b779a 100644 --- a/api_docs/kbn_securitysolution_autocomplete.mdx +++ b/api_docs/kbn_securitysolution_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-autocomplete title: "@kbn/securitysolution-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-autocomplete plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] --- import kbnSecuritysolutionAutocompleteObj from './kbn_securitysolution_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_data_table.mdx b/api_docs/kbn_securitysolution_data_table.mdx index 255d1a6618971..3b4edd18420ac 100644 --- a/api_docs/kbn_securitysolution_data_table.mdx +++ b/api_docs/kbn_securitysolution_data_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-data-table title: "@kbn/securitysolution-data-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-data-table plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-data-table'] --- import kbnSecuritysolutionDataTableObj from './kbn_securitysolution_data_table.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_ecs.mdx b/api_docs/kbn_securitysolution_ecs.mdx index 0165ca0b59fc9..5d11cfdca5b7f 100644 --- a/api_docs/kbn_securitysolution_ecs.mdx +++ b/api_docs/kbn_securitysolution_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-ecs title: "@kbn/securitysolution-ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-ecs plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-ecs'] --- import kbnSecuritysolutionEcsObj from './kbn_securitysolution_ecs.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_es_utils.mdx b/api_docs/kbn_securitysolution_es_utils.mdx index eed593d43dfdc..8404d230bf212 100644 --- a/api_docs/kbn_securitysolution_es_utils.mdx +++ b/api_docs/kbn_securitysolution_es_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-es-utils title: "@kbn/securitysolution-es-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-es-utils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-es-utils'] --- import kbnSecuritysolutionEsUtilsObj from './kbn_securitysolution_es_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_exception_list_components.mdx b/api_docs/kbn_securitysolution_exception_list_components.mdx index a52e5be544f77..928cafcdeddab 100644 --- a/api_docs/kbn_securitysolution_exception_list_components.mdx +++ b/api_docs/kbn_securitysolution_exception_list_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-exception-list-components title: "@kbn/securitysolution-exception-list-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-exception-list-components plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-exception-list-components'] --- import kbnSecuritysolutionExceptionListComponentsObj from './kbn_securitysolution_exception_list_components.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_grouping.mdx b/api_docs/kbn_securitysolution_grouping.mdx index bdc81d1d250ff..38cc77464c55e 100644 --- a/api_docs/kbn_securitysolution_grouping.mdx +++ b/api_docs/kbn_securitysolution_grouping.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-grouping title: "@kbn/securitysolution-grouping" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-grouping plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-grouping'] --- import kbnSecuritysolutionGroupingObj from './kbn_securitysolution_grouping.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index 24db83f3e3f7a..23a4d7539f34c 100644 --- a/api_docs/kbn_securitysolution_hook_utils.mdx +++ b/api_docs/kbn_securitysolution_hook_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-hook-utils title: "@kbn/securitysolution-hook-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-hook-utils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-hook-utils'] --- import kbnSecuritysolutionHookUtilsObj from './kbn_securitysolution_hook_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx index 90f75084bb669..096803d5d1dcc 100644 --- a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-alerting-types title: "@kbn/securitysolution-io-ts-alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-alerting-types plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-alerting-types'] --- import kbnSecuritysolutionIoTsAlertingTypesObj from './kbn_securitysolution_io_ts_alerting_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_list_types.mdx b/api_docs/kbn_securitysolution_io_ts_list_types.mdx index 698fecee87f4b..517925775389f 100644 --- a/api_docs/kbn_securitysolution_io_ts_list_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_list_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-list-types title: "@kbn/securitysolution-io-ts-list-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-list-types plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-list-types'] --- import kbnSecuritysolutionIoTsListTypesObj from './kbn_securitysolution_io_ts_list_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_types.mdx b/api_docs/kbn_securitysolution_io_ts_types.mdx index ae33949a4407d..63f8756358135 100644 --- a/api_docs/kbn_securitysolution_io_ts_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-types title: "@kbn/securitysolution-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-types plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-types'] --- import kbnSecuritysolutionIoTsTypesObj from './kbn_securitysolution_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_utils.mdx b/api_docs/kbn_securitysolution_io_ts_utils.mdx index 076b1b05b227c..3b8453ee7140b 100644 --- a/api_docs/kbn_securitysolution_io_ts_utils.mdx +++ b/api_docs/kbn_securitysolution_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-utils title: "@kbn/securitysolution-io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-utils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-utils'] --- import kbnSecuritysolutionIoTsUtilsObj from './kbn_securitysolution_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_api.mdx b/api_docs/kbn_securitysolution_list_api.mdx index 92f8eca6e01af..e1460523bb773 100644 --- a/api_docs/kbn_securitysolution_list_api.mdx +++ b/api_docs/kbn_securitysolution_list_api.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-api title: "@kbn/securitysolution-list-api" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-api plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-api'] --- import kbnSecuritysolutionListApiObj from './kbn_securitysolution_list_api.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_constants.mdx b/api_docs/kbn_securitysolution_list_constants.mdx index 516aa9347012f..8352096276e14 100644 --- a/api_docs/kbn_securitysolution_list_constants.mdx +++ b/api_docs/kbn_securitysolution_list_constants.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-constants title: "@kbn/securitysolution-list-constants" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-constants plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-constants'] --- import kbnSecuritysolutionListConstantsObj from './kbn_securitysolution_list_constants.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_hooks.mdx b/api_docs/kbn_securitysolution_list_hooks.mdx index 88e33dd10e14a..f706f8f6f4905 100644 --- a/api_docs/kbn_securitysolution_list_hooks.mdx +++ b/api_docs/kbn_securitysolution_list_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-hooks title: "@kbn/securitysolution-list-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-hooks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-hooks'] --- import kbnSecuritysolutionListHooksObj from './kbn_securitysolution_list_hooks.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_utils.mdx b/api_docs/kbn_securitysolution_list_utils.mdx index c2d7218df7952..df70e22184234 100644 --- a/api_docs/kbn_securitysolution_list_utils.mdx +++ b/api_docs/kbn_securitysolution_list_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-utils title: "@kbn/securitysolution-list-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-utils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-utils'] --- import kbnSecuritysolutionListUtilsObj from './kbn_securitysolution_list_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_rules.mdx b/api_docs/kbn_securitysolution_rules.mdx index 69f482223c092..621e624f7b3d0 100644 --- a/api_docs/kbn_securitysolution_rules.mdx +++ b/api_docs/kbn_securitysolution_rules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-rules title: "@kbn/securitysolution-rules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-rules plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-rules'] --- import kbnSecuritysolutionRulesObj from './kbn_securitysolution_rules.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_t_grid.mdx b/api_docs/kbn_securitysolution_t_grid.mdx index 50191d65e3946..6a4b404c44234 100644 --- a/api_docs/kbn_securitysolution_t_grid.mdx +++ b/api_docs/kbn_securitysolution_t_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-t-grid title: "@kbn/securitysolution-t-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-t-grid plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-t-grid'] --- import kbnSecuritysolutionTGridObj from './kbn_securitysolution_t_grid.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_utils.mdx b/api_docs/kbn_securitysolution_utils.mdx index 3bd1fa2c85bf6..c458c7ab9d639 100644 --- a/api_docs/kbn_securitysolution_utils.mdx +++ b/api_docs/kbn_securitysolution_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-utils title: "@kbn/securitysolution-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-utils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-utils'] --- import kbnSecuritysolutionUtilsObj from './kbn_securitysolution_utils.devdocs.json'; diff --git a/api_docs/kbn_server_http_tools.mdx b/api_docs/kbn_server_http_tools.mdx index 6d9c047cc6dff..d3775e0321552 100644 --- a/api_docs/kbn_server_http_tools.mdx +++ b/api_docs/kbn_server_http_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-http-tools title: "@kbn/server-http-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-http-tools plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-http-tools'] --- import kbnServerHttpToolsObj from './kbn_server_http_tools.devdocs.json'; diff --git a/api_docs/kbn_server_route_repository.mdx b/api_docs/kbn_server_route_repository.mdx index e3a48d88b0e18..95acb3c677003 100644 --- a/api_docs/kbn_server_route_repository.mdx +++ b/api_docs/kbn_server_route_repository.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository title: "@kbn/server-route-repository" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_serverless_project_switcher.mdx b/api_docs/kbn_serverless_project_switcher.mdx index 5ed339bbdc7d6..aa6da93f9c477 100644 --- a/api_docs/kbn_serverless_project_switcher.mdx +++ b/api_docs/kbn_serverless_project_switcher.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-project-switcher title: "@kbn/serverless-project-switcher" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-project-switcher plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-project-switcher'] --- import kbnServerlessProjectSwitcherObj from './kbn_serverless_project_switcher.devdocs.json'; diff --git a/api_docs/kbn_serverless_storybook_config.mdx b/api_docs/kbn_serverless_storybook_config.mdx index 4e86bdd37dd6d..11f040fbf0ade 100644 --- a/api_docs/kbn_serverless_storybook_config.mdx +++ b/api_docs/kbn_serverless_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-storybook-config title: "@kbn/serverless-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-storybook-config plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-storybook-config'] --- import kbnServerlessStorybookConfigObj from './kbn_serverless_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_shared_svg.mdx b/api_docs/kbn_shared_svg.mdx index 93ff82a9dd673..6b612c9b52e3c 100644 --- a/api_docs/kbn_shared_svg.mdx +++ b/api_docs/kbn_shared_svg.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-svg title: "@kbn/shared-svg" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-svg plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-svg'] --- import kbnSharedSvgObj from './kbn_shared_svg.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_avatar_solution.mdx b/api_docs/kbn_shared_ux_avatar_solution.mdx index 47703e2a78d2f..0f2876009bf6d 100644 --- a/api_docs/kbn_shared_ux_avatar_solution.mdx +++ b/api_docs/kbn_shared_ux_avatar_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-solution title: "@kbn/shared-ux-avatar-solution" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-solution plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-solution'] --- import kbnSharedUxAvatarSolutionObj from './kbn_shared_ux_avatar_solution.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx index c8955fc5261f5..7e36237dedc4b 100644 --- a/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx +++ b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-user-profile-components title: "@kbn/shared-ux-avatar-user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-user-profile-components plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-user-profile-components'] --- import kbnSharedUxAvatarUserProfileComponentsObj from './kbn_shared_ux_avatar_user_profile_components.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx index b749af49bc262..db9395e88f664 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen title: "@kbn/shared-ux-button-exit-full-screen" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen'] --- import kbnSharedUxButtonExitFullScreenObj from './kbn_shared_ux_button_exit_full_screen.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx index f734d39ab043d..b0f90559e6ae0 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen-mocks title: "@kbn/shared-ux-button-exit-full-screen-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen-mocks'] --- import kbnSharedUxButtonExitFullScreenMocksObj from './kbn_shared_ux_button_exit_full_screen_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index 7e0c73aadc6b3..e09d1bb60fb53 100644 --- a/api_docs/kbn_shared_ux_button_toolbar.mdx +++ b/api_docs/kbn_shared_ux_button_toolbar.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-toolbar title: "@kbn/shared-ux-button-toolbar" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-toolbar plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-toolbar'] --- import kbnSharedUxButtonToolbarObj from './kbn_shared_ux_button_toolbar.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data.devdocs.json b/api_docs/kbn_shared_ux_card_no_data.devdocs.json index 796fa77d7c585..30043835cff52 100644 --- a/api_docs/kbn_shared_ux_card_no_data.devdocs.json +++ b/api_docs/kbn_shared_ux_card_no_data.devdocs.json @@ -28,7 +28,7 @@ "description": [], "signature": [ "({ href: srcHref, category, description, ...props }: ", - "NoDataCardProps", + "NoDataCardComponentProps", ") => JSX.Element" ], "path": "packages/shared-ux/card/no_data/impl/src/no_data_card.tsx", @@ -38,12 +38,12 @@ { "parentPluginId": "@kbn/shared-ux-card-no-data", "id": "def-common.NoDataCard.$1", - "type": "Object", + "type": "CompoundType", "tags": [], "label": "{ href: srcHref, category, description, ...props }", "description": [], "signature": [ - "NoDataCardProps" + "NoDataCardComponentProps" ], "path": "packages/shared-ux/card/no_data/impl/src/no_data_card.tsx", "deprecated": false, @@ -184,179 +184,9 @@ "\nProps for the `NoDataCard` sevice-connected component." ], "signature": [ - "{ prefix?: string | undefined; id?: string | undefined; defaultValue?: string | number | readonly string[] | undefined; security?: string | undefined; children?: React.ReactNode; description?: React.ReactNode; category?: string | undefined; onChange?: React.FormEventHandler | undefined; defaultChecked?: boolean | undefined; suppressContentEditableWarning?: boolean | undefined; suppressHydrationWarning?: boolean | undefined; accessKey?: string | undefined; className?: string | undefined; contentEditable?: \"inherit\" | Booleanish | undefined; contextMenu?: string | undefined; dir?: string | undefined; draggable?: Booleanish | undefined; hidden?: boolean | undefined; lang?: string | undefined; placeholder?: string | undefined; slot?: string | undefined; spellCheck?: Booleanish | undefined; style?: React.CSSProperties | undefined; tabIndex?: number | undefined; title?: boolean | React.ReactChild | React.ReactFragment | React.ReactPortal | undefined; translate?: \"no\" | \"yes\" | undefined; radioGroup?: string | undefined; role?: React.AriaRole | undefined; about?: string | undefined; datatype?: string | undefined; inlist?: any; property?: string | undefined; resource?: string | undefined; typeof?: string | undefined; vocab?: string | undefined; autoCapitalize?: string | undefined; autoCorrect?: string | undefined; autoSave?: string | undefined; itemProp?: string | undefined; itemScope?: boolean | undefined; itemType?: string | undefined; itemID?: string | undefined; itemRef?: string | undefined; results?: number | undefined; unselectable?: \"on\" | \"off\" | undefined; inputMode?: \"search\" | \"none\" | \"text\" | \"url\" | \"email\" | \"tel\" | \"numeric\" | \"decimal\" | undefined; is?: string | undefined; 'aria-activedescendant'?: string | undefined; 'aria-atomic'?: Booleanish | undefined; 'aria-autocomplete'?: \"none\" | \"list\" | \"both\" | \"inline\" | undefined; 'aria-busy'?: Booleanish | undefined; 'aria-checked'?: boolean | \"true\" | \"false\" | \"mixed\" | undefined; 'aria-colcount'?: number | undefined; 'aria-colindex'?: number | undefined; 'aria-colspan'?: number | undefined; 'aria-controls'?: string | undefined; 'aria-current'?: boolean | \"page\" | \"date\" | \"true\" | \"false\" | \"location\" | \"time\" | \"step\" | undefined; 'aria-describedby'?: string | undefined; 'aria-details'?: string | undefined; 'aria-disabled'?: Booleanish | undefined; 'aria-dropeffect'?: \"execute\" | \"link\" | \"none\" | \"copy\" | \"move\" | \"popup\" | undefined; 'aria-errormessage'?: string | undefined; 'aria-expanded'?: Booleanish | undefined; 'aria-flowto'?: string | undefined; 'aria-grabbed'?: Booleanish | undefined; 'aria-haspopup'?: boolean | \"true\" | \"false\" | \"grid\" | \"menu\" | \"dialog\" | \"listbox\" | \"tree\" | undefined; 'aria-hidden'?: Booleanish | undefined; 'aria-invalid'?: boolean | \"true\" | \"false\" | \"grammar\" | \"spelling\" | undefined; 'aria-keyshortcuts'?: string | undefined; 'aria-label'?: string | undefined; 'aria-labelledby'?: string | undefined; 'aria-level'?: number | undefined; 'aria-live'?: \"off\" | \"assertive\" | \"polite\" | undefined; 'aria-modal'?: Booleanish | undefined; 'aria-multiline'?: Booleanish | undefined; 'aria-multiselectable'?: Booleanish | undefined; 'aria-orientation'?: \"horizontal\" | \"vertical\" | undefined; 'aria-owns'?: string | undefined; 'aria-placeholder'?: string | undefined; 'aria-posinset'?: number | undefined; 'aria-pressed'?: boolean | \"true\" | \"false\" | \"mixed\" | undefined; 'aria-readonly'?: Booleanish | undefined; 'aria-relevant'?: \"text\" | \"all\" | \"additions\" | \"additions removals\" | \"additions text\" | \"removals\" | \"removals additions\" | \"removals text\" | \"text additions\" | \"text removals\" | undefined; 'aria-required'?: Booleanish | undefined; 'aria-roledescription'?: string | undefined; 'aria-rowcount'?: number | undefined; 'aria-rowindex'?: number | undefined; 'aria-rowspan'?: number | undefined; 'aria-selected'?: Booleanish | undefined; 'aria-setsize'?: number | undefined; 'aria-sort'?: \"none\" | \"other\" | \"ascending\" | \"descending\" | undefined; 'aria-valuemax'?: number | undefined; 'aria-valuemin'?: number | undefined; 'aria-valuenow'?: number | undefined; 'aria-valuetext'?: string | undefined; dangerouslySetInnerHTML?: { __html: string; } | undefined; onCopy?: React.ClipboardEventHandler | undefined; onCopyCapture?: React.ClipboardEventHandler | undefined; onCut?: React.ClipboardEventHandler | undefined; onCutCapture?: React.ClipboardEventHandler | undefined; onPaste?: React.ClipboardEventHandler | undefined; onPasteCapture?: React.ClipboardEventHandler | undefined; onCompositionEnd?: React.CompositionEventHandler | undefined; onCompositionEndCapture?: React.CompositionEventHandler | undefined; onCompositionStart?: React.CompositionEventHandler | undefined; onCompositionStartCapture?: React.CompositionEventHandler | undefined; onCompositionUpdate?: React.CompositionEventHandler | undefined; onCompositionUpdateCapture?: React.CompositionEventHandler | undefined; onFocus?: React.FocusEventHandler | undefined; onFocusCapture?: React.FocusEventHandler | undefined; onBlur?: React.FocusEventHandler | undefined; onBlurCapture?: React.FocusEventHandler | undefined; onChangeCapture?: React.FormEventHandler | undefined; onBeforeInput?: React.FormEventHandler | undefined; onBeforeInputCapture?: React.FormEventHandler | undefined; onInput?: React.FormEventHandler | undefined; onInputCapture?: React.FormEventHandler | undefined; onReset?: React.FormEventHandler | undefined; onResetCapture?: React.FormEventHandler | undefined; onSubmit?: React.FormEventHandler | undefined; onSubmitCapture?: React.FormEventHandler | undefined; onInvalid?: React.FormEventHandler | undefined; onInvalidCapture?: React.FormEventHandler | undefined; onLoad?: React.ReactEventHandler | undefined; onLoadCapture?: React.ReactEventHandler | undefined; onError?: React.ReactEventHandler | undefined; onErrorCapture?: React.ReactEventHandler | undefined; onKeyDown?: React.KeyboardEventHandler | undefined; onKeyDownCapture?: React.KeyboardEventHandler | undefined; onKeyPress?: React.KeyboardEventHandler | undefined; onKeyPressCapture?: React.KeyboardEventHandler | undefined; onKeyUp?: React.KeyboardEventHandler | undefined; onKeyUpCapture?: React.KeyboardEventHandler | undefined; onAbort?: React.ReactEventHandler | undefined; onAbortCapture?: React.ReactEventHandler | undefined; onCanPlay?: React.ReactEventHandler | undefined; onCanPlayCapture?: React.ReactEventHandler | undefined; onCanPlayThrough?: React.ReactEventHandler | undefined; onCanPlayThroughCapture?: React.ReactEventHandler | undefined; onDurationChange?: React.ReactEventHandler | undefined; onDurationChangeCapture?: React.ReactEventHandler | undefined; onEmptied?: React.ReactEventHandler | undefined; onEmptiedCapture?: React.ReactEventHandler | undefined; onEncrypted?: React.ReactEventHandler | undefined; onEncryptedCapture?: React.ReactEventHandler | undefined; onEnded?: React.ReactEventHandler | undefined; onEndedCapture?: React.ReactEventHandler | undefined; onLoadedData?: React.ReactEventHandler | undefined; onLoadedDataCapture?: React.ReactEventHandler | undefined; onLoadedMetadata?: React.ReactEventHandler | undefined; onLoadedMetadataCapture?: React.ReactEventHandler | undefined; onLoadStart?: React.ReactEventHandler | undefined; onLoadStartCapture?: React.ReactEventHandler | undefined; onPause?: React.ReactEventHandler | undefined; onPauseCapture?: React.ReactEventHandler | undefined; onPlay?: React.ReactEventHandler | undefined; onPlayCapture?: React.ReactEventHandler | undefined; onPlaying?: React.ReactEventHandler | undefined; onPlayingCapture?: React.ReactEventHandler | undefined; onProgress?: React.ReactEventHandler | undefined; onProgressCapture?: React.ReactEventHandler | undefined; onRateChange?: React.ReactEventHandler | undefined; onRateChangeCapture?: React.ReactEventHandler | undefined; onSeeked?: React.ReactEventHandler | undefined; onSeekedCapture?: React.ReactEventHandler | undefined; onSeeking?: React.ReactEventHandler | undefined; onSeekingCapture?: React.ReactEventHandler | undefined; onStalled?: React.ReactEventHandler | undefined; onStalledCapture?: React.ReactEventHandler | undefined; onSuspend?: React.ReactEventHandler | undefined; onSuspendCapture?: React.ReactEventHandler | undefined; onTimeUpdate?: React.ReactEventHandler | undefined; onTimeUpdateCapture?: React.ReactEventHandler | undefined; onVolumeChange?: React.ReactEventHandler | undefined; onVolumeChangeCapture?: React.ReactEventHandler | undefined; onWaiting?: React.ReactEventHandler | undefined; onWaitingCapture?: React.ReactEventHandler | undefined; onAuxClick?: React.MouseEventHandler | undefined; onAuxClickCapture?: React.MouseEventHandler | undefined; onClick?: React.MouseEventHandler | undefined; onClickCapture?: React.MouseEventHandler | undefined; onContextMenu?: React.MouseEventHandler | undefined; onContextMenuCapture?: React.MouseEventHandler | undefined; onDoubleClick?: React.MouseEventHandler | undefined; onDoubleClickCapture?: React.MouseEventHandler | undefined; onDrag?: React.DragEventHandler | undefined; onDragCapture?: React.DragEventHandler | undefined; onDragEnd?: React.DragEventHandler | undefined; onDragEndCapture?: React.DragEventHandler | undefined; onDragEnter?: React.DragEventHandler | undefined; onDragEnterCapture?: React.DragEventHandler | undefined; onDragExit?: React.DragEventHandler | undefined; onDragExitCapture?: React.DragEventHandler | undefined; onDragLeave?: React.DragEventHandler | undefined; onDragLeaveCapture?: React.DragEventHandler | undefined; onDragOver?: React.DragEventHandler | undefined; onDragOverCapture?: React.DragEventHandler | undefined; onDragStart?: React.DragEventHandler | undefined; onDragStartCapture?: React.DragEventHandler | undefined; onDrop?: React.DragEventHandler | undefined; onDropCapture?: React.DragEventHandler | undefined; onMouseDown?: React.MouseEventHandler | undefined; onMouseDownCapture?: React.MouseEventHandler | undefined; onMouseEnter?: React.MouseEventHandler | undefined; onMouseLeave?: React.MouseEventHandler | undefined; onMouseMove?: React.MouseEventHandler | undefined; onMouseMoveCapture?: React.MouseEventHandler | undefined; onMouseOut?: React.MouseEventHandler | undefined; onMouseOutCapture?: React.MouseEventHandler | undefined; onMouseOver?: React.MouseEventHandler | undefined; onMouseOverCapture?: React.MouseEventHandler | undefined; onMouseUp?: React.MouseEventHandler | undefined; onMouseUpCapture?: React.MouseEventHandler | undefined; onSelect?: React.ReactEventHandler | undefined; onSelectCapture?: React.ReactEventHandler | undefined; onTouchCancel?: React.TouchEventHandler | undefined; onTouchCancelCapture?: React.TouchEventHandler | undefined; onTouchEnd?: React.TouchEventHandler | undefined; onTouchEndCapture?: React.TouchEventHandler | undefined; onTouchMove?: React.TouchEventHandler | undefined; onTouchMoveCapture?: React.TouchEventHandler | undefined; onTouchStart?: React.TouchEventHandler | undefined; onTouchStartCapture?: React.TouchEventHandler | undefined; onPointerDown?: React.PointerEventHandler | undefined; onPointerDownCapture?: React.PointerEventHandler | undefined; onPointerMove?: React.PointerEventHandler | undefined; onPointerMoveCapture?: React.PointerEventHandler | undefined; onPointerUp?: React.PointerEventHandler | undefined; onPointerUpCapture?: React.PointerEventHandler | undefined; onPointerCancel?: React.PointerEventHandler | undefined; onPointerCancelCapture?: React.PointerEventHandler | undefined; onPointerEnter?: React.PointerEventHandler | undefined; onPointerEnterCapture?: React.PointerEventHandler | undefined; onPointerLeave?: React.PointerEventHandler | undefined; onPointerLeaveCapture?: React.PointerEventHandler | undefined; onPointerOver?: React.PointerEventHandler | undefined; onPointerOverCapture?: React.PointerEventHandler | undefined; onPointerOut?: React.PointerEventHandler | undefined; onPointerOutCapture?: React.PointerEventHandler | undefined; onGotPointerCapture?: React.PointerEventHandler | undefined; onGotPointerCaptureCapture?: React.PointerEventHandler | undefined; onLostPointerCapture?: React.PointerEventHandler | undefined; onLostPointerCaptureCapture?: React.PointerEventHandler | undefined; onScroll?: React.UIEventHandler | undefined; onScrollCapture?: React.UIEventHandler | undefined; onWheel?: React.WheelEventHandler | undefined; onWheelCapture?: React.WheelEventHandler | undefined; onAnimationStart?: React.AnimationEventHandler | undefined; onAnimationStartCapture?: React.AnimationEventHandler | undefined; onAnimationEnd?: React.AnimationEventHandler | undefined; onAnimationEndCapture?: React.AnimationEventHandler | undefined; onAnimationIteration?: React.AnimationEventHandler | undefined; onAnimationIterationCapture?: React.AnimationEventHandler | undefined; onTransitionEnd?: React.TransitionEventHandler | undefined; onTransitionEndCapture?: React.TransitionEventHandler | undefined; hasBorder?: boolean | undefined; paddingSize?: \"m\" | \"none\" | \"s\" | \"xs\" | \"l\" | \"xl\" | undefined; 'data-test-subj'?: string | undefined; css?: ", - "Interpolation", - "<", - "Theme", - ">; href?: string | undefined; rel?: string | undefined; target?: string | undefined; icon?: React.ReactElement<", - "EuiIconProps", - ", string | React.JSXElementConstructor> | null | undefined; image?: string | React.ReactElement> | undefined; display?: \"warning\" | \"success\" | \"subdued\" | \"primary\" | \"accent\" | \"danger\" | \"transparent\" | \"plain\" | undefined; button?: React.ReactNode; footer?: React.ReactNode; textAlign?: \"left\" | \"right\" | \"center\" | undefined; titleElement?: \"span\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\" | undefined; titleSize?: \"s\" | \"xs\" | undefined; betaBadgeProps?: (", - "CommonProps", - " & ", - "DisambiguateSet", - "<(", - "DisambiguateSet", - " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"color\" | \"onClick\" | \"href\">) | (", - "DisambiguateSet", - " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"color\" | \"onClick\">), WithSpanProps> & WithSpanProps & { iconType?: ", - "IconType", - " | undefined; label: React.ReactNode; tooltipContent?: React.ReactNode; tooltipPosition?: ", - "ToolTipPositions", - " | undefined; anchorProps?: (", - "CommonProps", - " & React.HTMLAttributes) | undefined; title?: string | undefined; color?: \"subdued\" | \"accent\" | \"hollow\" | undefined; size?: \"m\" | \"s\" | undefined; alignment?: \"middle\" | \"baseline\" | undefined; } & ", - "DisambiguateSet", - " & LabelAsString) | (", - "CommonProps", - " & ", - "DisambiguateSet", - "<(", - "DisambiguateSet", - " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"color\" | \"onClick\" | \"href\">) | (", - "DisambiguateSet", - " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"color\" | \"onClick\">), WithSpanProps> & WithSpanProps & { iconType?: ", - "IconType", - " | undefined; label: React.ReactNode; tooltipContent?: React.ReactNode; tooltipPosition?: ", - "ToolTipPositions", - " | undefined; anchorProps?: (", - "CommonProps", - " & React.HTMLAttributes) | undefined; title?: string | undefined; color?: \"subdued\" | \"accent\" | \"hollow\" | undefined; size?: \"m\" | \"s\" | undefined; alignment?: \"middle\" | \"baseline\" | undefined; } & ", - "DisambiguateSet", - " & ", - "DisambiguateSet", - "<{ title: string; tooltipContent?: React.ReactNode; }, { tooltipContent: React.ReactNode; title?: string | undefined; }> & { tooltipContent: React.ReactNode; title?: string | undefined; } & { label: React.ReactNode; }) | (", - "CommonProps", - " & ", - "DisambiguateSet", - " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"color\" | \"onClick\" | \"href\">) | (", - "DisambiguateSet", - " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"color\" | \"onClick\">)> & ", - "DisambiguateSet", - " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"color\" | \"onClick\" | \"href\"> & { iconType?: ", - "IconType", - " | undefined; label: React.ReactNode; tooltipContent?: React.ReactNode; tooltipPosition?: ", - "ToolTipPositions", - " | undefined; anchorProps?: (", - "CommonProps", - " & React.HTMLAttributes) | undefined; title?: string | undefined; color?: \"subdued\" | \"accent\" | \"hollow\" | undefined; size?: \"m\" | \"s\" | undefined; alignment?: \"middle\" | \"baseline\" | undefined; } & ", - "DisambiguateSet", - " & LabelAsString) | (", - "CommonProps", - " & ", - "DisambiguateSet", - " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"color\" | \"onClick\" | \"href\">) | (", - "DisambiguateSet", - " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"color\" | \"onClick\">)> & ", - "DisambiguateSet", - " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"color\" | \"onClick\" | \"href\"> & { iconType?: ", - "IconType", - " | undefined; label: React.ReactNode; tooltipContent?: React.ReactNode; tooltipPosition?: ", - "ToolTipPositions", - " | undefined; anchorProps?: (", - "CommonProps", - " & React.HTMLAttributes) | undefined; title?: string | undefined; color?: \"subdued\" | \"accent\" | \"hollow\" | undefined; size?: \"m\" | \"s\" | undefined; alignment?: \"middle\" | \"baseline\" | undefined; } & ", - "DisambiguateSet", - " & ", - "DisambiguateSet", - "<{ title: string; tooltipContent?: React.ReactNode; }, { tooltipContent: React.ReactNode; title?: string | undefined; }> & { tooltipContent: React.ReactNode; title?: string | undefined; } & { label: React.ReactNode; }) | (", - "CommonProps", - " & ", - "DisambiguateSet", - " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"color\" | \"onClick\" | \"href\">) | (", - "DisambiguateSet", - " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"color\" | \"onClick\">)> & ", - "DisambiguateSet", - " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"color\" | \"onClick\" | \"href\"> & { iconType?: ", - "IconType", - " | undefined; label: React.ReactNode; tooltipContent?: React.ReactNode; tooltipPosition?: ", - "ToolTipPositions", - " | undefined; anchorProps?: (", - "CommonProps", - " & React.HTMLAttributes) | undefined; title?: string | undefined; color?: \"subdued\" | \"accent\" | \"hollow\" | undefined; size?: \"m\" | \"s\" | undefined; alignment?: \"middle\" | \"baseline\" | undefined; } & ", - "DisambiguateSet", - " & ", - "DisambiguateSet", - "<{ tooltipContent: React.ReactNode; title?: string | undefined; }, { title: string; tooltipContent?: React.ReactNode; }> & { title: string; tooltipContent?: React.ReactNode; } & { label: React.ReactNode; }) | (", - "CommonProps", - " & ", - "DisambiguateSet", - " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"color\" | \"onClick\" | \"href\">) | (", - "DisambiguateSet", - " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"color\" | \"onClick\">)> & ", - "DisambiguateSet", - " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"color\" | \"onClick\"> & { iconType?: ", - "IconType", - " | undefined; label: React.ReactNode; tooltipContent?: React.ReactNode; tooltipPosition?: ", - "ToolTipPositions", - " | undefined; anchorProps?: (", - "CommonProps", - " & React.HTMLAttributes) | undefined; title?: string | undefined; color?: \"subdued\" | \"accent\" | \"hollow\" | undefined; size?: \"m\" | \"s\" | undefined; alignment?: \"middle\" | \"baseline\" | undefined; } & ", - "DisambiguateSet", - " & LabelAsString) | (", - "CommonProps", - " & ", - "DisambiguateSet", - " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"color\" | \"onClick\" | \"href\">) | (", - "DisambiguateSet", - " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"color\" | \"onClick\">)> & ", - "DisambiguateSet", - " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"color\" | \"onClick\"> & { iconType?: ", - "IconType", - " | undefined; label: React.ReactNode; tooltipContent?: React.ReactNode; tooltipPosition?: ", - "ToolTipPositions", - " | undefined; anchorProps?: (", - "CommonProps", - " & React.HTMLAttributes) | undefined; title?: string | undefined; color?: \"subdued\" | \"accent\" | \"hollow\" | undefined; size?: \"m\" | \"s\" | undefined; alignment?: \"middle\" | \"baseline\" | undefined; } & ", - "DisambiguateSet", - " & ", - "DisambiguateSet", - "<{ title: string; tooltipContent?: React.ReactNode; }, { tooltipContent: React.ReactNode; title?: string | undefined; }> & { tooltipContent: React.ReactNode; title?: string | undefined; } & { label: React.ReactNode; }) | (", - "CommonProps", - " & ", - "DisambiguateSet", - " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"color\" | \"onClick\" | \"href\">) | (", - "DisambiguateSet", - " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"color\" | \"onClick\">)> & ", - "DisambiguateSet", - " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"color\" | \"onClick\"> & { iconType?: ", - "IconType", - " | undefined; label: React.ReactNode; tooltipContent?: React.ReactNode; tooltipPosition?: ", - "ToolTipPositions", - " | undefined; anchorProps?: (", - "CommonProps", - " & React.HTMLAttributes) | undefined; title?: string | undefined; color?: \"subdued\" | \"accent\" | \"hollow\" | undefined; size?: \"m\" | \"s\" | undefined; alignment?: \"middle\" | \"baseline\" | undefined; } & ", - "DisambiguateSet", - " & ", - "DisambiguateSet", - "<{ tooltipContent: React.ReactNode; title?: string | undefined; }, { title: string; tooltipContent?: React.ReactNode; }> & { title: string; tooltipContent?: React.ReactNode; } & { label: React.ReactNode; }) | undefined; selectable?: (", - "DisambiguateSet", - "<", - "EuiButtonPropsForAnchor", - ", ", - "EuiButtonPropsForButton", - "> & ", - "EuiButtonProps", - " & { onClick?: React.MouseEventHandler | undefined; } & React.ButtonHTMLAttributes & { buttonRef?: React.Ref | undefined; }) | (", - "DisambiguateSet", - "<", - "EuiButtonPropsForButton", - ", ", - "EuiButtonPropsForAnchor", - "> & ", - "EuiButtonProps", - " & { href?: string | undefined; onClick?: React.MouseEventHandler | undefined; } & React.AnchorHTMLAttributes & { buttonRef?: React.Ref | undefined; }) | undefined; }" + "Partial> & { button?: React.ReactNode; onClick?: React.MouseEventHandler | undefined; description?: React.ReactNode; category?: string | undefined; canAccessFleet?: boolean | undefined; }" ], "path": "packages/shared-ux/card/no_data/types/index.d.ts", "deprecated": false, diff --git a/api_docs/kbn_shared_ux_card_no_data.mdx b/api_docs/kbn_shared_ux_card_no_data.mdx index 8dd83e914d6ab..f4e62f597966a 100644 --- a/api_docs/kbn_shared_ux_card_no_data.mdx +++ b/api_docs/kbn_shared_ux_card_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data title: "@kbn/shared-ux-card-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data'] --- import kbnSharedUxCardNoDataObj from './kbn_shared_ux_card_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data_mocks.devdocs.json b/api_docs/kbn_shared_ux_card_no_data_mocks.devdocs.json index 1aa83630b7156..9283e27a593ff 100644 --- a/api_docs/kbn_shared_ux_card_no_data_mocks.devdocs.json +++ b/api_docs/kbn_shared_ux_card_no_data_mocks.devdocs.json @@ -44,7 +44,7 @@ "text": "AbstractStorybookMock" }, "<", - "NoDataCardProps", + "NoDataCardComponentProps", ", ", "NoDataCardServices", ", PropArguments, ServiceArguments>" @@ -344,7 +344,7 @@ "text": "Params" }, " | undefined) => ", - "NoDataCardProps" + "NoDataCardComponentProps" ], "path": "packages/shared-ux/card/no_data/mocks/src/storybook.ts", "deprecated": false, diff --git a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx index 4673fd2972836..69199b1416456 100644 --- a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data-mocks title: "@kbn/shared-ux-card-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data-mocks'] --- import kbnSharedUxCardNoDataMocksObj from './kbn_shared_ux_card_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_chrome_navigation.mdx b/api_docs/kbn_shared_ux_chrome_navigation.mdx index d9260c5f26437..cf16454368cd0 100644 --- a/api_docs/kbn_shared_ux_chrome_navigation.mdx +++ b/api_docs/kbn_shared_ux_chrome_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-chrome-navigation title: "@kbn/shared-ux-chrome-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-chrome-navigation plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-chrome-navigation'] --- import kbnSharedUxChromeNavigationObj from './kbn_shared_ux_chrome_navigation.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_context.mdx b/api_docs/kbn_shared_ux_file_context.mdx index f781d7d70f194..23f9d0a2811e3 100644 --- a/api_docs/kbn_shared_ux_file_context.mdx +++ b/api_docs/kbn_shared_ux_file_context.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-context title: "@kbn/shared-ux-file-context" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-context plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-context'] --- import kbnSharedUxFileContextObj from './kbn_shared_ux_file_context.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_image.mdx b/api_docs/kbn_shared_ux_file_image.mdx index e4a6df96a1a04..830594a259441 100644 --- a/api_docs/kbn_shared_ux_file_image.mdx +++ b/api_docs/kbn_shared_ux_file_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image title: "@kbn/shared-ux-file-image" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-image'] --- import kbnSharedUxFileImageObj from './kbn_shared_ux_file_image.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_image_mocks.mdx b/api_docs/kbn_shared_ux_file_image_mocks.mdx index afa41697edfdd..5b1ac2d313724 100644 --- a/api_docs/kbn_shared_ux_file_image_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_image_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image-mocks title: "@kbn/shared-ux-file-image-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-image-mocks'] --- import kbnSharedUxFileImageMocksObj from './kbn_shared_ux_file_image_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_mocks.mdx b/api_docs/kbn_shared_ux_file_mocks.mdx index 1c5ef9ed1da6f..92f4185ba9485 100644 --- a/api_docs/kbn_shared_ux_file_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-mocks title: "@kbn/shared-ux-file-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-mocks'] --- import kbnSharedUxFileMocksObj from './kbn_shared_ux_file_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_picker.mdx b/api_docs/kbn_shared_ux_file_picker.mdx index 4ad2618cd9b29..856f141ffb8ed 100644 --- a/api_docs/kbn_shared_ux_file_picker.mdx +++ b/api_docs/kbn_shared_ux_file_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-picker title: "@kbn/shared-ux-file-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-picker plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-picker'] --- import kbnSharedUxFilePickerObj from './kbn_shared_ux_file_picker.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_types.mdx b/api_docs/kbn_shared_ux_file_types.mdx index 167b8c852a805..8b30f58d1014e 100644 --- a/api_docs/kbn_shared_ux_file_types.mdx +++ b/api_docs/kbn_shared_ux_file_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-types title: "@kbn/shared-ux-file-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-types plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-types'] --- import kbnSharedUxFileTypesObj from './kbn_shared_ux_file_types.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_upload.mdx b/api_docs/kbn_shared_ux_file_upload.mdx index 086ecc19f9084..ee118d8d30131 100644 --- a/api_docs/kbn_shared_ux_file_upload.mdx +++ b/api_docs/kbn_shared_ux_file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-upload title: "@kbn/shared-ux-file-upload" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-upload plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-upload'] --- import kbnSharedUxFileUploadObj from './kbn_shared_ux_file_upload.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_util.mdx b/api_docs/kbn_shared_ux_file_util.mdx index c70d57a2252a0..4d209eb741ade 100644 --- a/api_docs/kbn_shared_ux_file_util.mdx +++ b/api_docs/kbn_shared_ux_file_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-util title: "@kbn/shared-ux-file-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-util plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-util'] --- import kbnSharedUxFileUtilObj from './kbn_shared_ux_file_util.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app.mdx b/api_docs/kbn_shared_ux_link_redirect_app.mdx index de45099694ed2..e6ad6ff96ef6f 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app title: "@kbn/shared-ux-link-redirect-app" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app'] --- import kbnSharedUxLinkRedirectAppObj from './kbn_shared_ux_link_redirect_app.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx index 5e61e4f9cf607..7a58af1dd23c1 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app-mocks title: "@kbn/shared-ux-link-redirect-app-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app-mocks'] --- import kbnSharedUxLinkRedirectAppMocksObj from './kbn_shared_ux_link_redirect_app_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_markdown.mdx b/api_docs/kbn_shared_ux_markdown.mdx index 55cd4377dbd63..cb8c346fe03d4 100644 --- a/api_docs/kbn_shared_ux_markdown.mdx +++ b/api_docs/kbn_shared_ux_markdown.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown title: "@kbn/shared-ux-markdown" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-markdown'] --- import kbnSharedUxMarkdownObj from './kbn_shared_ux_markdown.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_markdown_mocks.mdx b/api_docs/kbn_shared_ux_markdown_mocks.mdx index cac8433f7fa33..4704d8ff04654 100644 --- a/api_docs/kbn_shared_ux_markdown_mocks.mdx +++ b/api_docs/kbn_shared_ux_markdown_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown-mocks title: "@kbn/shared-ux-markdown-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-markdown-mocks'] --- import kbnSharedUxMarkdownMocksObj from './kbn_shared_ux_markdown_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data.devdocs.json b/api_docs/kbn_shared_ux_page_analytics_no_data.devdocs.json index 217e49d5572e4..4b6db494be35b 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data.devdocs.json +++ b/api_docs/kbn_shared_ux_page_analytics_no_data.devdocs.json @@ -66,7 +66,7 @@ "\nA pure component of an entire page that can be displayed when Kibana \"has no data\", specifically for Analytics." ], "signature": [ - "({ kibanaGuideDocLink, onDataViewCreated, allowAdHocDataView, showPlainSpinner, }: ", + "({ kibanaGuideDocLink, onDataViewCreated, allowAdHocDataView, showPlainSpinner, prependBasePath, pageFlavor, }: ", "Props", ") => JSX.Element" ], @@ -79,7 +79,7 @@ "id": "def-common.AnalyticsNoDataPage.$1", "type": "Object", "tags": [], - "label": "{\n kibanaGuideDocLink,\n onDataViewCreated,\n allowAdHocDataView,\n showPlainSpinner,\n}", + "label": "{\n kibanaGuideDocLink,\n onDataViewCreated,\n allowAdHocDataView,\n showPlainSpinner,\n prependBasePath,\n pageFlavor = 'kibana',\n}", "description": [], "signature": [ "Props" diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx index fdffba1ab9db7..8f137c1b19bae 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data title: "@kbn/shared-ux-page-analytics-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data'] --- import kbnSharedUxPageAnalyticsNoDataObj from './kbn_shared_ux_page_analytics_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx index 8f8ffb559f6da..43b35150ca54f 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data-mocks title: "@kbn/shared-ux-page-analytics-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data-mocks'] --- import kbnSharedUxPageAnalyticsNoDataMocksObj from './kbn_shared_ux_page_analytics_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx index a5f6dde801029..76beef22dab87 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data title: "@kbn/shared-ux-page-kibana-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data'] --- import kbnSharedUxPageKibanaNoDataObj from './kbn_shared_ux_page_kibana_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx index 372b64e714635..7287acc7bf6d6 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data-mocks title: "@kbn/shared-ux-page-kibana-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data-mocks'] --- import kbnSharedUxPageKibanaNoDataMocksObj from './kbn_shared_ux_page_kibana_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_template.mdx b/api_docs/kbn_shared_ux_page_kibana_template.mdx index 5d63395058034..7a4c6b9f0ca79 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template title: "@kbn/shared-ux-page-kibana-template" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-template'] --- import kbnSharedUxPageKibanaTemplateObj from './kbn_shared_ux_page_kibana_template.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx index 51ddbcf854299..42191d0e64159 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template-mocks title: "@kbn/shared-ux-page-kibana-template-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-template-mocks'] --- import kbnSharedUxPageKibanaTemplateMocksObj from './kbn_shared_ux_page_kibana_template_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data.devdocs.json b/api_docs/kbn_shared_ux_page_no_data.devdocs.json index e6f62668e8f73..9aa9a10503b95 100644 --- a/api_docs/kbn_shared_ux_page_no_data.devdocs.json +++ b/api_docs/kbn_shared_ux_page_no_data.devdocs.json @@ -172,7 +172,10 @@ "tags": [], "label": "docsLink", "description": [ - "\nRequired to set the docs link for the whole solution" + "\nRequired in \"kibana\" flavor to set the docs link for the whole solution, otherwise optional" + ], + "signature": [ + "string | undefined" ], "path": "packages/shared-ux/page/no_data/types/index.d.ts", "deprecated": false, diff --git a/api_docs/kbn_shared_ux_page_no_data.mdx b/api_docs/kbn_shared_ux_page_no_data.mdx index 22118ce9b06ed..274d7abec9027 100644 --- a/api_docs/kbn_shared_ux_page_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data title: "@kbn/shared-ux-page-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data'] --- import kbnSharedUxPageNoDataObj from './kbn_shared_ux_page_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_config.mdx b/api_docs/kbn_shared_ux_page_no_data_config.mdx index 2ccd8f078d37b..8ad594e4af757 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config title: "@kbn/shared-ux-page-no-data-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-config'] --- import kbnSharedUxPageNoDataConfigObj from './kbn_shared_ux_page_no_data_config.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx index e590c55314f33..17435ce9d3f78 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config-mocks title: "@kbn/shared-ux-page-no-data-config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-config-mocks'] --- import kbnSharedUxPageNoDataConfigMocksObj from './kbn_shared_ux_page_no_data_config_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx index 7fc9af83b2b79..875216415544c 100644 --- a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-mocks title: "@kbn/shared-ux-page-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-mocks'] --- import kbnSharedUxPageNoDataMocksObj from './kbn_shared_ux_page_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_solution_nav.mdx b/api_docs/kbn_shared_ux_page_solution_nav.mdx index 9e99c44add2c5..957c1bb26ac28 100644 --- a/api_docs/kbn_shared_ux_page_solution_nav.mdx +++ b/api_docs/kbn_shared_ux_page_solution_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-solution-nav title: "@kbn/shared-ux-page-solution-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-solution-nav plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-solution-nav'] --- import kbnSharedUxPageSolutionNavObj from './kbn_shared_ux_page_solution_nav.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx index 330f831293846..21228fc56e777 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views title: "@kbn/shared-ux-prompt-no-data-views" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views'] --- import kbnSharedUxPromptNoDataViewsObj from './kbn_shared_ux_prompt_no_data_views.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx index 1a431bddf6153..f9d069f8e210f 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views-mocks title: "@kbn/shared-ux-prompt-no-data-views-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views-mocks'] --- import kbnSharedUxPromptNoDataViewsMocksObj from './kbn_shared_ux_prompt_no_data_views_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_not_found.mdx b/api_docs/kbn_shared_ux_prompt_not_found.mdx index 245c1b7f291df..ce4ddbf658b33 100644 --- a/api_docs/kbn_shared_ux_prompt_not_found.mdx +++ b/api_docs/kbn_shared_ux_prompt_not_found.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-not-found title: "@kbn/shared-ux-prompt-not-found" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-not-found plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-not-found'] --- import kbnSharedUxPromptNotFoundObj from './kbn_shared_ux_prompt_not_found.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router.mdx b/api_docs/kbn_shared_ux_router.mdx index a0ef64ac2048e..71e814e3be0d8 100644 --- a/api_docs/kbn_shared_ux_router.mdx +++ b/api_docs/kbn_shared_ux_router.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router title: "@kbn/shared-ux-router" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router'] --- import kbnSharedUxRouterObj from './kbn_shared_ux_router.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router_mocks.mdx b/api_docs/kbn_shared_ux_router_mocks.mdx index 32da12d65192a..d37edc70374c6 100644 --- a/api_docs/kbn_shared_ux_router_mocks.mdx +++ b/api_docs/kbn_shared_ux_router_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router-mocks title: "@kbn/shared-ux-router-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router-mocks plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router-mocks'] --- import kbnSharedUxRouterMocksObj from './kbn_shared_ux_router_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_storybook_config.mdx b/api_docs/kbn_shared_ux_storybook_config.mdx index c4f64195fbc60..18e25c6928684 100644 --- a/api_docs/kbn_shared_ux_storybook_config.mdx +++ b/api_docs/kbn_shared_ux_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-config title: "@kbn/shared-ux-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-config plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-config'] --- import kbnSharedUxStorybookConfigObj from './kbn_shared_ux_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_storybook_mock.mdx b/api_docs/kbn_shared_ux_storybook_mock.mdx index 3624d063eb708..a1cd940e9f883 100644 --- a/api_docs/kbn_shared_ux_storybook_mock.mdx +++ b/api_docs/kbn_shared_ux_storybook_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-mock title: "@kbn/shared-ux-storybook-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-mock plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-mock'] --- import kbnSharedUxStorybookMockObj from './kbn_shared_ux_storybook_mock.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index 7bb41b54d7813..e3ace32ceff37 100644 --- a/api_docs/kbn_shared_ux_utility.mdx +++ b/api_docs/kbn_shared_ux_utility.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-utility title: "@kbn/shared-ux-utility" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-utility plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] --- import kbnSharedUxUtilityObj from './kbn_shared_ux_utility.devdocs.json'; diff --git a/api_docs/kbn_slo_schema.devdocs.json b/api_docs/kbn_slo_schema.devdocs.json index f3d983b94c45e..02eec6e3a39cb 100644 --- a/api_docs/kbn_slo_schema.devdocs.json +++ b/api_docs/kbn_slo_schema.devdocs.json @@ -3591,26 +3591,6 @@ "trackAdoption": false, "initialIsOpen": false }, - { - "parentPluginId": "@kbn/slo-schema", - "id": "def-common.getSLODiagnosisParamsSchema", - "type": "Object", - "tags": [], - "label": "getSLODiagnosisParamsSchema", - "description": [], - "signature": [ - "TypeC", - "<{ path: ", - "TypeC", - "<{ id: ", - "StringC", - "; }>; }>" - ], - "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/slo.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, { "parentPluginId": "@kbn/slo-schema", "id": "def-common.getSLOInstancesParamsSchema", diff --git a/api_docs/kbn_slo_schema.mdx b/api_docs/kbn_slo_schema.mdx index f7be00fa3d043..fa014ce11dddb 100644 --- a/api_docs/kbn_slo_schema.mdx +++ b/api_docs/kbn_slo_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-slo-schema title: "@kbn/slo-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/slo-schema plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/slo-schema'] --- import kbnSloSchemaObj from './kbn_slo_schema.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/actionable-observability](https://github.com/orgs/elastic/team | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 133 | 0 | 130 | 0 | +| 132 | 0 | 129 | 0 | ## Common diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index b67bb857c65a8..5e91eb8810ae4 100644 --- a/api_docs/kbn_some_dev_log.mdx +++ b/api_docs/kbn_some_dev_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-some-dev-log title: "@kbn/some-dev-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/some-dev-log plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/some-dev-log'] --- import kbnSomeDevLogObj from './kbn_some_dev_log.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index 298e5619bbc34..e5ab024d5bf00 100644 --- a/api_docs/kbn_std.mdx +++ b/api_docs/kbn_std.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-std title: "@kbn/std" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/std plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/std'] --- import kbnStdObj from './kbn_std.devdocs.json'; diff --git a/api_docs/kbn_stdio_dev_helpers.mdx b/api_docs/kbn_stdio_dev_helpers.mdx index 9a2df44429635..257d1a20a394c 100644 --- a/api_docs/kbn_stdio_dev_helpers.mdx +++ b/api_docs/kbn_stdio_dev_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-stdio-dev-helpers title: "@kbn/stdio-dev-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/stdio-dev-helpers plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/stdio-dev-helpers'] --- import kbnStdioDevHelpersObj from './kbn_stdio_dev_helpers.devdocs.json'; diff --git a/api_docs/kbn_storybook.mdx b/api_docs/kbn_storybook.mdx index f0a174578c760..5b1672a76e614 100644 --- a/api_docs/kbn_storybook.mdx +++ b/api_docs/kbn_storybook.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-storybook title: "@kbn/storybook" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/storybook plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/storybook'] --- import kbnStorybookObj from './kbn_storybook.devdocs.json'; diff --git a/api_docs/kbn_telemetry_tools.mdx b/api_docs/kbn_telemetry_tools.mdx index cc1df56cc923d..4f3af81ac9d76 100644 --- a/api_docs/kbn_telemetry_tools.mdx +++ b/api_docs/kbn_telemetry_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-telemetry-tools title: "@kbn/telemetry-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/telemetry-tools plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/telemetry-tools'] --- import kbnTelemetryToolsObj from './kbn_telemetry_tools.devdocs.json'; diff --git a/api_docs/kbn_test.mdx b/api_docs/kbn_test.mdx index f9ab7378a610c..12254960a3bcd 100644 --- a/api_docs/kbn_test.mdx +++ b/api_docs/kbn_test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test title: "@kbn/test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] --- import kbnTestObj from './kbn_test.devdocs.json'; diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index 4aecfe0131933..4d8ddb2f69963 100644 --- a/api_docs/kbn_test_jest_helpers.mdx +++ b/api_docs/kbn_test_jest_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-jest-helpers title: "@kbn/test-jest-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-jest-helpers plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-jest-helpers'] --- import kbnTestJestHelpersObj from './kbn_test_jest_helpers.devdocs.json'; diff --git a/api_docs/kbn_test_subj_selector.mdx b/api_docs/kbn_test_subj_selector.mdx index 1b55dd7131894..e5fa037280d38 100644 --- a/api_docs/kbn_test_subj_selector.mdx +++ b/api_docs/kbn_test_subj_selector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-subj-selector title: "@kbn/test-subj-selector" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-subj-selector plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-subj-selector'] --- import kbnTestSubjSelectorObj from './kbn_test_subj_selector.devdocs.json'; diff --git a/api_docs/kbn_text_based_editor.mdx b/api_docs/kbn_text_based_editor.mdx index bb0c65074cc60..d0c71da058945 100644 --- a/api_docs/kbn_text_based_editor.mdx +++ b/api_docs/kbn_text_based_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-text-based-editor title: "@kbn/text-based-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/text-based-editor plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/text-based-editor'] --- import kbnTextBasedEditorObj from './kbn_text_based_editor.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index 6e48b6768bf7e..b20f45a9c4058 100644 --- a/api_docs/kbn_tooling_log.mdx +++ b/api_docs/kbn_tooling_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-tooling-log title: "@kbn/tooling-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/tooling-log plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] --- import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; diff --git a/api_docs/kbn_ts_projects.mdx b/api_docs/kbn_ts_projects.mdx index 766cb3f9cb0a8..f5eabeb06ea4c 100644 --- a/api_docs/kbn_ts_projects.mdx +++ b/api_docs/kbn_ts_projects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ts-projects title: "@kbn/ts-projects" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ts-projects plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ts-projects'] --- import kbnTsProjectsObj from './kbn_ts_projects.devdocs.json'; diff --git a/api_docs/kbn_typed_react_router_config.mdx b/api_docs/kbn_typed_react_router_config.mdx index 4b2c77303f15e..31ea7d67456a5 100644 --- a/api_docs/kbn_typed_react_router_config.mdx +++ b/api_docs/kbn_typed_react_router_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-typed-react-router-config title: "@kbn/typed-react-router-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/typed-react-router-config plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/typed-react-router-config'] --- import kbnTypedReactRouterConfigObj from './kbn_typed_react_router_config.devdocs.json'; diff --git a/api_docs/kbn_ui_actions_browser.mdx b/api_docs/kbn_ui_actions_browser.mdx index 17963d781df57..1358a1714994e 100644 --- a/api_docs/kbn_ui_actions_browser.mdx +++ b/api_docs/kbn_ui_actions_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-actions-browser title: "@kbn/ui-actions-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-actions-browser plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-actions-browser'] --- import kbnUiActionsBrowserObj from './kbn_ui_actions_browser.devdocs.json'; diff --git a/api_docs/kbn_ui_shared_deps_src.mdx b/api_docs/kbn_ui_shared_deps_src.mdx index b05479b849817..f3d423de49ff6 100644 --- a/api_docs/kbn_ui_shared_deps_src.mdx +++ b/api_docs/kbn_ui_shared_deps_src.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-shared-deps-src title: "@kbn/ui-shared-deps-src" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-shared-deps-src plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-shared-deps-src'] --- import kbnUiSharedDepsSrcObj from './kbn_ui_shared_deps_src.devdocs.json'; diff --git a/api_docs/kbn_ui_theme.mdx b/api_docs/kbn_ui_theme.mdx index ea892279118e6..f22c2c15c35fc 100644 --- a/api_docs/kbn_ui_theme.mdx +++ b/api_docs/kbn_ui_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-theme title: "@kbn/ui-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-theme plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] --- import kbnUiThemeObj from './kbn_ui_theme.devdocs.json'; diff --git a/api_docs/kbn_unified_field_list.mdx b/api_docs/kbn_unified_field_list.mdx index 56fdb4e443640..6a265e7eadae2 100644 --- a/api_docs/kbn_unified_field_list.mdx +++ b/api_docs/kbn_unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-field-list title: "@kbn/unified-field-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-field-list plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-field-list'] --- import kbnUnifiedFieldListObj from './kbn_unified_field_list.devdocs.json'; diff --git a/api_docs/kbn_url_state.mdx b/api_docs/kbn_url_state.mdx index 43efefbcba544..8ff7a1031b7a8 100644 --- a/api_docs/kbn_url_state.mdx +++ b/api_docs/kbn_url_state.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-url-state title: "@kbn/url-state" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/url-state plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/url-state'] --- import kbnUrlStateObj from './kbn_url_state.devdocs.json'; diff --git a/api_docs/kbn_use_tracked_promise.mdx b/api_docs/kbn_use_tracked_promise.mdx index 83f6de626fad2..0af4f5d49265e 100644 --- a/api_docs/kbn_use_tracked_promise.mdx +++ b/api_docs/kbn_use_tracked_promise.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-use-tracked-promise title: "@kbn/use-tracked-promise" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/use-tracked-promise plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/use-tracked-promise'] --- import kbnUseTrackedPromiseObj from './kbn_use_tracked_promise.devdocs.json'; diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index 6157307403717..9fed54ca5d325 100644 --- a/api_docs/kbn_user_profile_components.mdx +++ b/api_docs/kbn_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-user-profile-components title: "@kbn/user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/user-profile-components plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/user-profile-components'] --- import kbnUserProfileComponentsObj from './kbn_user_profile_components.devdocs.json'; diff --git a/api_docs/kbn_utility_types.mdx b/api_docs/kbn_utility_types.mdx index e9f5af14d629e..c0e666a3d573c 100644 --- a/api_docs/kbn_utility_types.mdx +++ b/api_docs/kbn_utility_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types title: "@kbn/utility-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types'] --- import kbnUtilityTypesObj from './kbn_utility_types.devdocs.json'; diff --git a/api_docs/kbn_utility_types_jest.mdx b/api_docs/kbn_utility_types_jest.mdx index 0157113f06ed6..430e9f209ba18 100644 --- a/api_docs/kbn_utility_types_jest.mdx +++ b/api_docs/kbn_utility_types_jest.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types-jest title: "@kbn/utility-types-jest" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types-jest plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types-jest'] --- import kbnUtilityTypesJestObj from './kbn_utility_types_jest.devdocs.json'; diff --git a/api_docs/kbn_utils.mdx b/api_docs/kbn_utils.mdx index c440db5e6fd00..16987b55706d4 100644 --- a/api_docs/kbn_utils.mdx +++ b/api_docs/kbn_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utils title: "@kbn/utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; diff --git a/api_docs/kbn_visualization_ui_components.mdx b/api_docs/kbn_visualization_ui_components.mdx index 51c3608a82595..5d5a3f2a58c0e 100644 --- a/api_docs/kbn_visualization_ui_components.mdx +++ b/api_docs/kbn_visualization_ui_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-visualization-ui-components title: "@kbn/visualization-ui-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/visualization-ui-components plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/visualization-ui-components'] --- import kbnVisualizationUiComponentsObj from './kbn_visualization_ui_components.devdocs.json'; diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index 852a5eab0c5cb..ddee9ca854aa9 100644 --- a/api_docs/kbn_yarn_lock_validator.mdx +++ b/api_docs/kbn_yarn_lock_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-yarn-lock-validator title: "@kbn/yarn-lock-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/yarn-lock-validator plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/yarn-lock-validator'] --- import kbnYarnLockValidatorObj from './kbn_yarn_lock_validator.devdocs.json'; diff --git a/api_docs/kibana_overview.mdx b/api_docs/kibana_overview.mdx index ed12c1c0988aa..4bd66f17c803d 100644 --- a/api_docs/kibana_overview.mdx +++ b/api_docs/kibana_overview.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaOverview title: "kibanaOverview" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaOverview plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaOverview'] --- import kibanaOverviewObj from './kibana_overview.devdocs.json'; diff --git a/api_docs/kibana_react.mdx b/api_docs/kibana_react.mdx index c2027b9045e04..2ee81bd38b33e 100644 --- a/api_docs/kibana_react.mdx +++ b/api_docs/kibana_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaReact title: "kibanaReact" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaReact plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaReact'] --- import kibanaReactObj from './kibana_react.devdocs.json'; diff --git a/api_docs/kibana_utils.mdx b/api_docs/kibana_utils.mdx index df62b3e0ef796..2a09443de9b3c 100644 --- a/api_docs/kibana_utils.mdx +++ b/api_docs/kibana_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaUtils title: "kibanaUtils" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaUtils plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaUtils'] --- import kibanaUtilsObj from './kibana_utils.devdocs.json'; diff --git a/api_docs/kubernetes_security.mdx b/api_docs/kubernetes_security.mdx index 970c9f245c393..e1da6b763fa0d 100644 --- a/api_docs/kubernetes_security.mdx +++ b/api_docs/kubernetes_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kubernetesSecurity title: "kubernetesSecurity" image: https://source.unsplash.com/400x175/?github description: API docs for the kubernetesSecurity plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kubernetesSecurity'] --- import kubernetesSecurityObj from './kubernetes_security.devdocs.json'; diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index e1bd8dad106b7..595a33dd849bf 100644 --- a/api_docs/lens.mdx +++ b/api_docs/lens.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lens title: "lens" image: https://source.unsplash.com/400x175/?github description: API docs for the lens plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lens'] --- import lensObj from './lens.devdocs.json'; diff --git a/api_docs/license_api_guard.mdx b/api_docs/license_api_guard.mdx index dbeb3765baf64..d39ec76e6e0b6 100644 --- a/api_docs/license_api_guard.mdx +++ b/api_docs/license_api_guard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseApiGuard title: "licenseApiGuard" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseApiGuard plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseApiGuard'] --- import licenseApiGuardObj from './license_api_guard.devdocs.json'; diff --git a/api_docs/license_management.mdx b/api_docs/license_management.mdx index 81a762e5e53c6..181c51f6a7f60 100644 --- a/api_docs/license_management.mdx +++ b/api_docs/license_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseManagement title: "licenseManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseManagement plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseManagement'] --- import licenseManagementObj from './license_management.devdocs.json'; diff --git a/api_docs/licensing.mdx b/api_docs/licensing.mdx index 4c84d136d33f4..2e7ed075cd6c8 100644 --- a/api_docs/licensing.mdx +++ b/api_docs/licensing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licensing title: "licensing" image: https://source.unsplash.com/400x175/?github description: API docs for the licensing plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] --- import licensingObj from './licensing.devdocs.json'; diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index 13016f736fb69..ff691940dc751 100644 --- a/api_docs/lists.mdx +++ b/api_docs/lists.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lists title: "lists" image: https://source.unsplash.com/400x175/?github description: API docs for the lists plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/logs_shared.mdx b/api_docs/logs_shared.mdx index e9e23f9745349..8ec3e570acee0 100644 --- a/api_docs/logs_shared.mdx +++ b/api_docs/logs_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsShared title: "logsShared" image: https://source.unsplash.com/400x175/?github description: API docs for the logsShared plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsShared'] --- import logsSharedObj from './logs_shared.devdocs.json'; diff --git a/api_docs/management.mdx b/api_docs/management.mdx index f8f6b02b958ae..851e89f0450f6 100644 --- a/api_docs/management.mdx +++ b/api_docs/management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/management title: "management" image: https://source.unsplash.com/400x175/?github description: API docs for the management plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'management'] --- import managementObj from './management.devdocs.json'; diff --git a/api_docs/maps.mdx b/api_docs/maps.mdx index 21e1d235c2832..c7dd1d3279bc4 100644 --- a/api_docs/maps.mdx +++ b/api_docs/maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/maps title: "maps" image: https://source.unsplash.com/400x175/?github description: API docs for the maps plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'maps'] --- import mapsObj from './maps.devdocs.json'; diff --git a/api_docs/maps_ems.mdx b/api_docs/maps_ems.mdx index e25fa6dc8b213..04030f115b578 100644 --- a/api_docs/maps_ems.mdx +++ b/api_docs/maps_ems.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mapsEms title: "mapsEms" image: https://source.unsplash.com/400x175/?github description: API docs for the mapsEms plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] --- import mapsEmsObj from './maps_ems.devdocs.json'; diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index 41e335b2fe8a5..a389fc5a3d29e 100644 --- a/api_docs/ml.mdx +++ b/api_docs/ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ml title: "ml" image: https://source.unsplash.com/400x175/?github description: API docs for the ml plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] --- import mlObj from './ml.devdocs.json'; diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index 166def6155dde..e955022d97f6f 100644 --- a/api_docs/monitoring.mdx +++ b/api_docs/monitoring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoring title: "monitoring" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoring plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoring'] --- import monitoringObj from './monitoring.devdocs.json'; diff --git a/api_docs/monitoring_collection.mdx b/api_docs/monitoring_collection.mdx index d43e2ebf85923..d679b2cd903c7 100644 --- a/api_docs/monitoring_collection.mdx +++ b/api_docs/monitoring_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoringCollection title: "monitoringCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoringCollection plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoringCollection'] --- import monitoringCollectionObj from './monitoring_collection.devdocs.json'; diff --git a/api_docs/navigation.mdx b/api_docs/navigation.mdx index 29ab5944a4a73..eea6f16c1b1f2 100644 --- a/api_docs/navigation.mdx +++ b/api_docs/navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/navigation title: "navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the navigation plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'navigation'] --- import navigationObj from './navigation.devdocs.json'; diff --git a/api_docs/newsfeed.mdx b/api_docs/newsfeed.mdx index e7a38d7160b0f..6b5368717a70c 100644 --- a/api_docs/newsfeed.mdx +++ b/api_docs/newsfeed.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/newsfeed title: "newsfeed" image: https://source.unsplash.com/400x175/?github description: API docs for the newsfeed plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/no_data_page.devdocs.json b/api_docs/no_data_page.devdocs.json new file mode 100644 index 0000000000000..0bbeb2e20defc --- /dev/null +++ b/api_docs/no_data_page.devdocs.json @@ -0,0 +1,49 @@ +{ + "id": "noDataPage", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [], + "setup": { + "parentPluginId": "noDataPage", + "id": "def-public.NoDataPagePluginStart", + "type": "Type", + "tags": [], + "label": "NoDataPagePluginStart", + "description": [], + "signature": [ + { + "pluginId": "noDataPage", + "scope": "public", + "docId": "kibNoDataPagePluginApi", + "section": "def-public.NoDataPagePluginSetup", + "text": "NoDataPagePluginSetup" + } + ], + "path": "src/plugins/no_data_page/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "lifecycle": "setup", + "initialIsOpen": true + } + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/no_data_page.mdx b/api_docs/no_data_page.mdx new file mode 100644 index 0000000000000..6014be6b3ae9f --- /dev/null +++ b/api_docs/no_data_page.mdx @@ -0,0 +1,30 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibNoDataPagePluginApi +slug: /kibana-dev-docs/api/noDataPage +title: "noDataPage" +image: https://source.unsplash.com/400x175/?github +description: API docs for the noDataPage plugin +date: 2023-08-29 +tags: ['contributor', 'dev', 'apidocs', 'kibana', 'noDataPage'] +--- +import noDataPageObj from './no_data_page.devdocs.json'; + + + +Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 3 | 0 | 3 | 0 | + +## Client + +### Setup + + diff --git a/api_docs/notifications.mdx b/api_docs/notifications.mdx index a4c686845e407..864910d8ef2c5 100644 --- a/api_docs/notifications.mdx +++ b/api_docs/notifications.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/notifications title: "notifications" image: https://source.unsplash.com/400x175/?github description: API docs for the notifications plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'notifications'] --- import notificationsObj from './notifications.devdocs.json'; diff --git a/api_docs/observability.devdocs.json b/api_docs/observability.devdocs.json index 3816bbd3603fd..e910e74323d61 100644 --- a/api_docs/observability.devdocs.json +++ b/api_docs/observability.devdocs.json @@ -8627,72 +8627,6 @@ "section": "def-server.ObservabilityRouteCreateOptions", "text": "ObservabilityRouteCreateOptions" }, - "; \"GET /internal/observability/slos/{id}/_diagnosis\": { endpoint: \"GET /internal/observability/slos/{id}/_diagnosis\"; params?: ", - "TypeC", - "<{ path: ", - "TypeC", - "<{ id: ", - "StringC", - "; }>; }> | undefined; handler: ({}: ", - { - "pluginId": "observability", - "scope": "server", - "docId": "kibObservabilityPluginApi", - "section": "def-server.ObservabilityRouteHandlerResources", - "text": "ObservabilityRouteHandlerResources" - }, - " & { params: { path: { id: string; }; }; }) => Promise<{ sloResources: { \".slo-observability.sli\": string; \".slo-observability.sli-mappings\": string; \".slo-observability.sli-settings\": string; \".slo-observability.sli.pipeline\": string; } | undefined; sloSummaryResources: { \".slo-observability.summary\": string; \".slo-observability.summary-mappings\": string; \".slo-observability.summary-settings\": string; } | undefined; slo: string | { id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: ({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; })[]; equation: string; }; total: { metrics: ({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; })[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: ", - { - "pluginId": "@kbn/slo-schema", - "scope": "common", - "docId": "kibKbnSloSchemaPluginApi", - "section": "def-common.Duration", - "text": "Duration" - }, - "; type: \"rolling\"; } | { duration: ", - { - "pluginId": "@kbn/slo-schema", - "scope": "common", - "docId": "kibKbnSloSchemaPluginApi", - "section": "def-common.Duration", - "text": "Duration" - }, - "; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: ", - { - "pluginId": "@kbn/slo-schema", - "scope": "common", - "docId": "kibKbnSloSchemaPluginApi", - "section": "def-common.Duration", - "text": "Duration" - }, - " | undefined; }; settings: { syncDelay: ", - { - "pluginId": "@kbn/slo-schema", - "scope": "common", - "docId": "kibKbnSloSchemaPluginApi", - "section": "def-common.Duration", - "text": "Duration" - }, - "; frequency: ", - { - "pluginId": "@kbn/slo-schema", - "scope": "common", - "docId": "kibKbnSloSchemaPluginApi", - "section": "def-common.Duration", - "text": "Duration" - }, - "; }; revision: number; enabled: boolean; tags: string[]; createdAt: Date; updatedAt: Date; groupBy: string; }; sloTransformStats: ", - "TransformGetTransformStatsResponse", - "; sloSummaryTransformsStats: ", - "TransformGetTransformStatsResponse", - "; }>; } & ", - { - "pluginId": "observability", - "scope": "server", - "docId": "kibObservabilityPluginApi", - "section": "def-server.ObservabilityRouteCreateOptions", - "text": "ObservabilityRouteCreateOptions" - }, "; \"GET /internal/observability/slos/_diagnosis\": { endpoint: \"GET /internal/observability/slos/_diagnosis\"; params?: undefined; handler: ({}: ", { "pluginId": "observability", @@ -8703,11 +8637,11 @@ }, ") => Promise<{ licenseAndFeatures: ", "PublicLicenseJSON", - "; userPrivileges: ", - "SecurityGetUserPrivilegesResponse", - "; sloResources: { \".slo-observability.sli\": string; \".slo-observability.sli-mappings\": string; \".slo-observability.sli-settings\": string; \".slo-observability.sli.pipeline\": string; } | undefined; sloSummaryResources: { \".slo-observability.summary\": string; \".slo-observability.summary-mappings\": string; \".slo-observability.summary-settings\": string; } | undefined; sloSummaryTransformsStats: ", - "TransformGetTransformStatsResponse", - "; }>; } & ", + "; userPrivileges: { write: ", + "SecurityHasPrivilegesResponse", + "; read: ", + "SecurityHasPrivilegesResponse", + "; }; }>; } & ", { "pluginId": "observability", "scope": "server", @@ -10399,72 +10333,6 @@ "section": "def-server.ObservabilityRouteCreateOptions", "text": "ObservabilityRouteCreateOptions" }, - "; \"GET /internal/observability/slos/{id}/_diagnosis\": { endpoint: \"GET /internal/observability/slos/{id}/_diagnosis\"; params?: ", - "TypeC", - "<{ path: ", - "TypeC", - "<{ id: ", - "StringC", - "; }>; }> | undefined; handler: ({}: ", - { - "pluginId": "observability", - "scope": "server", - "docId": "kibObservabilityPluginApi", - "section": "def-server.ObservabilityRouteHandlerResources", - "text": "ObservabilityRouteHandlerResources" - }, - " & { params: { path: { id: string; }; }; }) => Promise<{ sloResources: { \".slo-observability.sli\": string; \".slo-observability.sli-mappings\": string; \".slo-observability.sli-settings\": string; \".slo-observability.sli.pipeline\": string; } | undefined; sloSummaryResources: { \".slo-observability.summary\": string; \".slo-observability.summary-mappings\": string; \".slo-observability.summary-settings\": string; } | undefined; slo: string | { id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: ({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; })[]; equation: string; }; total: { metrics: ({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; })[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: ", - { - "pluginId": "@kbn/slo-schema", - "scope": "common", - "docId": "kibKbnSloSchemaPluginApi", - "section": "def-common.Duration", - "text": "Duration" - }, - "; type: \"rolling\"; } | { duration: ", - { - "pluginId": "@kbn/slo-schema", - "scope": "common", - "docId": "kibKbnSloSchemaPluginApi", - "section": "def-common.Duration", - "text": "Duration" - }, - "; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: ", - { - "pluginId": "@kbn/slo-schema", - "scope": "common", - "docId": "kibKbnSloSchemaPluginApi", - "section": "def-common.Duration", - "text": "Duration" - }, - " | undefined; }; settings: { syncDelay: ", - { - "pluginId": "@kbn/slo-schema", - "scope": "common", - "docId": "kibKbnSloSchemaPluginApi", - "section": "def-common.Duration", - "text": "Duration" - }, - "; frequency: ", - { - "pluginId": "@kbn/slo-schema", - "scope": "common", - "docId": "kibKbnSloSchemaPluginApi", - "section": "def-common.Duration", - "text": "Duration" - }, - "; }; revision: number; enabled: boolean; tags: string[]; createdAt: Date; updatedAt: Date; groupBy: string; }; sloTransformStats: ", - "TransformGetTransformStatsResponse", - "; sloSummaryTransformsStats: ", - "TransformGetTransformStatsResponse", - "; }>; } & ", - { - "pluginId": "observability", - "scope": "server", - "docId": "kibObservabilityPluginApi", - "section": "def-server.ObservabilityRouteCreateOptions", - "text": "ObservabilityRouteCreateOptions" - }, "; \"GET /internal/observability/slos/_diagnosis\": { endpoint: \"GET /internal/observability/slos/_diagnosis\"; params?: undefined; handler: ({}: ", { "pluginId": "observability", @@ -10475,11 +10343,11 @@ }, ") => Promise<{ licenseAndFeatures: ", "PublicLicenseJSON", - "; userPrivileges: ", - "SecurityGetUserPrivilegesResponse", - "; sloResources: { \".slo-observability.sli\": string; \".slo-observability.sli-mappings\": string; \".slo-observability.sli-settings\": string; \".slo-observability.sli.pipeline\": string; } | undefined; sloSummaryResources: { \".slo-observability.summary\": string; \".slo-observability.summary-mappings\": string; \".slo-observability.summary-settings\": string; } | undefined; sloSummaryTransformsStats: ", - "TransformGetTransformStatsResponse", - "; }>; } & ", + "; userPrivileges: { write: ", + "SecurityHasPrivilegesResponse", + "; read: ", + "SecurityHasPrivilegesResponse", + "; }; }>; } & ", { "pluginId": "observability", "scope": "server", diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index 701c96c075b3d..e08e73da6a8f1 100644 --- a/api_docs/observability.mdx +++ b/api_docs/observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observability title: "observability" image: https://source.unsplash.com/400x175/?github description: API docs for the observability plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; diff --git a/api_docs/observability_a_i_assistant.devdocs.json b/api_docs/observability_a_i_assistant.devdocs.json index add55aaff694a..f1202bffd58d1 100644 --- a/api_docs/observability_a_i_assistant.devdocs.json +++ b/api_docs/observability_a_i_assistant.devdocs.json @@ -367,7 +367,29 @@ "label": "APIReturnType", "description": [], "signature": [ - "{ \"GET /internal/observability_ai_assistant/functions/kb_status\": { endpoint: \"GET /internal/observability_ai_assistant/functions/kb_status\"; params?: undefined; handler: ({}: ", + "{ \"POST /internal/observability_ai_assistant/functions/alerts\": { endpoint: \"POST /internal/observability_ai_assistant/functions/alerts\"; params?: ", + "TypeC", + "<{ body: ", + "IntersectionC", + "<[", + "TypeC", + "<{ featureIds: ", + "ArrayC", + "<", + "StringC", + ">; start: ", + "StringC", + "; end: ", + "StringC", + "; }>, ", + "PartialC", + "<{ filter: ", + "StringC", + "; }>]>; }> | undefined; handler: ({}: ", + "ObservabilityAIAssistantRouteHandlerResources", + " & { params: { body: { featureIds: string[]; start: string; end: string; } & { filter?: string | undefined; }; }; }) => Promise<{ content: { total: number; alerts: OutputOf>[]; }; }>; } & ", + "ObservabilityAIAssistantRouteCreateOptions", + "; \"GET /internal/observability_ai_assistant/functions/kb_status\": { endpoint: \"GET /internal/observability_ai_assistant/functions/kb_status\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", ") => Promise<{ ready: boolean; error?: any; deployment_state?: string | undefined; allocation_state?: string | undefined; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", @@ -405,7 +427,13 @@ "Type", "; public: ", "Type", - "; }>; }> | undefined; handler: ({}: ", + "; labels: ", + "RecordC", + "<", + "StringC", + ", ", + "StringC", + ">; }>; }> | undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", " & { params: { body: { id: string; text: ", "Branded", @@ -417,13 +445,15 @@ "section": "def-common.NonEmptyStringBrand", "text": "NonEmptyStringBrand" }, - ">; confidence: \"medium\" | \"high\" | \"low\"; is_correction: boolean; public: boolean; }; }; }) => Promise; } & ", + ">; confidence: \"medium\" | \"high\" | \"low\"; is_correction: boolean; public: boolean; labels: { [x: string]: string; }; }; }; }) => Promise; } & ", "ObservabilityAIAssistantRouteCreateOptions", "; \"POST /internal/observability_ai_assistant/functions/recall\": { endpoint: \"POST /internal/observability_ai_assistant/functions/recall\"; params?: ", "TypeC", "<{ body: ", "TypeC", - "<{ query: ", + "<{ queries: ", + "ArrayC", + "<", "BrandC", "<", "StringC", @@ -435,9 +465,9 @@ "section": "def-common.NonEmptyStringBrand", "text": "NonEmptyStringBrand" }, - ">; }>; }> | undefined; handler: ({}: ", + ">>; }>; }> | undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { body: { query: ", + " & { params: { body: { queries: ", "Branded", "; }; }; }) => Promise<{ entries: ", + ">[]; }; }; }) => Promise<{ entries: Pick<", "KnowledgeBaseEntry", - "[]; }>; } & ", + ", \"id\" | \"text\">[]; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", "; \"POST /internal/observability_ai_assistant/functions/elasticsearch\": { endpoint: \"POST /internal/observability_ai_assistant/functions/elasticsearch\"; params?: ", "TypeC", @@ -700,7 +730,29 @@ "label": "ObservabilityAIAssistantAPIClientRequestParamsOf", "description": [], "signature": [ - "{ \"GET /internal/observability_ai_assistant/functions/kb_status\": { endpoint: \"GET /internal/observability_ai_assistant/functions/kb_status\"; params?: undefined; handler: ({}: ", + "{ \"POST /internal/observability_ai_assistant/functions/alerts\": { endpoint: \"POST /internal/observability_ai_assistant/functions/alerts\"; params?: ", + "TypeC", + "<{ body: ", + "IntersectionC", + "<[", + "TypeC", + "<{ featureIds: ", + "ArrayC", + "<", + "StringC", + ">; start: ", + "StringC", + "; end: ", + "StringC", + "; }>, ", + "PartialC", + "<{ filter: ", + "StringC", + "; }>]>; }> | undefined; handler: ({}: ", + "ObservabilityAIAssistantRouteHandlerResources", + " & { params: { body: { featureIds: string[]; start: string; end: string; } & { filter?: string | undefined; }; }; }) => Promise<{ content: { total: number; alerts: OutputOf>[]; }; }>; } & ", + "ObservabilityAIAssistantRouteCreateOptions", + "; \"GET /internal/observability_ai_assistant/functions/kb_status\": { endpoint: \"GET /internal/observability_ai_assistant/functions/kb_status\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", ") => Promise<{ ready: boolean; error?: any; deployment_state?: string | undefined; allocation_state?: string | undefined; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", @@ -738,7 +790,13 @@ "Type", "; public: ", "Type", - "; }>; }> | undefined; handler: ({}: ", + "; labels: ", + "RecordC", + "<", + "StringC", + ", ", + "StringC", + ">; }>; }> | undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", " & { params: { body: { id: string; text: ", "Branded", @@ -750,13 +808,15 @@ "section": "def-common.NonEmptyStringBrand", "text": "NonEmptyStringBrand" }, - ">; confidence: \"medium\" | \"high\" | \"low\"; is_correction: boolean; public: boolean; }; }; }) => Promise; } & ", + ">; confidence: \"medium\" | \"high\" | \"low\"; is_correction: boolean; public: boolean; labels: { [x: string]: string; }; }; }; }) => Promise; } & ", "ObservabilityAIAssistantRouteCreateOptions", "; \"POST /internal/observability_ai_assistant/functions/recall\": { endpoint: \"POST /internal/observability_ai_assistant/functions/recall\"; params?: ", "TypeC", "<{ body: ", "TypeC", - "<{ query: ", + "<{ queries: ", + "ArrayC", + "<", "BrandC", "<", "StringC", @@ -768,9 +828,9 @@ "section": "def-common.NonEmptyStringBrand", "text": "NonEmptyStringBrand" }, - ">; }>; }> | undefined; handler: ({}: ", + ">>; }>; }> | undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { body: { query: ", + " & { params: { body: { queries: ", "Branded", "; }; }; }) => Promise<{ entries: ", + ">[]; }; }; }) => Promise<{ entries: Pick<", "KnowledgeBaseEntry", - "[]; }>; } & ", + ", \"id\" | \"text\">[]; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", "; \"POST /internal/observability_ai_assistant/functions/elasticsearch\": { endpoint: \"POST /internal/observability_ai_assistant/functions/elasticsearch\"; params?: ", "TypeC", @@ -1041,7 +1101,7 @@ "label": "ObservabilityAIAssistantAPIEndpoint", "description": [], "signature": [ - "\"POST /internal/observability_ai_assistant/chat\" | \"GET /internal/observability_ai_assistant/conversation/{conversationId}\" | \"POST /internal/observability_ai_assistant/conversations\" | \"POST /internal/observability_ai_assistant/conversation\" | \"PUT /internal/observability_ai_assistant/conversation/{conversationId}\" | \"PUT /internal/observability_ai_assistant/conversation/{conversationId}/auto_title\" | \"PUT /internal/observability_ai_assistant/conversation/{conversationId}/title\" | \"DELETE /internal/observability_ai_assistant/conversation/{conversationId}\" | \"GET /internal/observability_ai_assistant/connectors\" | \"POST /internal/observability_ai_assistant/functions/elasticsearch\" | \"POST /internal/observability_ai_assistant/functions/recall\" | \"POST /internal/observability_ai_assistant/functions/summarise\" | \"POST /internal/observability_ai_assistant/functions/setup_kb\" | \"GET /internal/observability_ai_assistant/functions/kb_status\"" + "\"POST /internal/observability_ai_assistant/chat\" | \"GET /internal/observability_ai_assistant/conversation/{conversationId}\" | \"POST /internal/observability_ai_assistant/conversations\" | \"POST /internal/observability_ai_assistant/conversation\" | \"PUT /internal/observability_ai_assistant/conversation/{conversationId}\" | \"PUT /internal/observability_ai_assistant/conversation/{conversationId}/auto_title\" | \"PUT /internal/observability_ai_assistant/conversation/{conversationId}/title\" | \"DELETE /internal/observability_ai_assistant/conversation/{conversationId}\" | \"GET /internal/observability_ai_assistant/connectors\" | \"POST /internal/observability_ai_assistant/functions/elasticsearch\" | \"POST /internal/observability_ai_assistant/functions/recall\" | \"POST /internal/observability_ai_assistant/functions/summarise\" | \"POST /internal/observability_ai_assistant/functions/setup_kb\" | \"GET /internal/observability_ai_assistant/functions/kb_status\" | \"POST /internal/observability_ai_assistant/functions/alerts\"" ], "path": "x-pack/plugins/observability_ai_assistant/public/api/index.ts", "deprecated": false, @@ -1139,7 +1199,29 @@ "label": "ObservabilityAIAssistantServerRouteRepository", "description": [], "signature": [ - "{ \"GET /internal/observability_ai_assistant/functions/kb_status\": { endpoint: \"GET /internal/observability_ai_assistant/functions/kb_status\"; params?: undefined; handler: ({}: ", + "{ \"POST /internal/observability_ai_assistant/functions/alerts\": { endpoint: \"POST /internal/observability_ai_assistant/functions/alerts\"; params?: ", + "TypeC", + "<{ body: ", + "IntersectionC", + "<[", + "TypeC", + "<{ featureIds: ", + "ArrayC", + "<", + "StringC", + ">; start: ", + "StringC", + "; end: ", + "StringC", + "; }>, ", + "PartialC", + "<{ filter: ", + "StringC", + "; }>]>; }> | undefined; handler: ({}: ", + "ObservabilityAIAssistantRouteHandlerResources", + " & { params: { body: { featureIds: string[]; start: string; end: string; } & { filter?: string | undefined; }; }; }) => Promise<{ content: { total: number; alerts: OutputOf>[]; }; }>; } & ", + "ObservabilityAIAssistantRouteCreateOptions", + "; \"GET /internal/observability_ai_assistant/functions/kb_status\": { endpoint: \"GET /internal/observability_ai_assistant/functions/kb_status\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", ") => Promise<{ ready: boolean; error?: any; deployment_state?: string | undefined; allocation_state?: string | undefined; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", @@ -1177,7 +1259,13 @@ "Type", "; public: ", "Type", - "; }>; }> | undefined; handler: ({}: ", + "; labels: ", + "RecordC", + "<", + "StringC", + ", ", + "StringC", + ">; }>; }> | undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", " & { params: { body: { id: string; text: ", "Branded", @@ -1189,13 +1277,15 @@ "section": "def-common.NonEmptyStringBrand", "text": "NonEmptyStringBrand" }, - ">; confidence: \"medium\" | \"high\" | \"low\"; is_correction: boolean; public: boolean; }; }; }) => Promise; } & ", + ">; confidence: \"medium\" | \"high\" | \"low\"; is_correction: boolean; public: boolean; labels: { [x: string]: string; }; }; }; }) => Promise; } & ", "ObservabilityAIAssistantRouteCreateOptions", "; \"POST /internal/observability_ai_assistant/functions/recall\": { endpoint: \"POST /internal/observability_ai_assistant/functions/recall\"; params?: ", "TypeC", "<{ body: ", "TypeC", - "<{ query: ", + "<{ queries: ", + "ArrayC", + "<", "BrandC", "<", "StringC", @@ -1207,9 +1297,9 @@ "section": "def-common.NonEmptyStringBrand", "text": "NonEmptyStringBrand" }, - ">; }>; }> | undefined; handler: ({}: ", + ">>; }>; }> | undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { body: { query: ", + " & { params: { body: { queries: ", "Branded", "; }; }; }) => Promise<{ entries: ", + ">[]; }; }; }) => Promise<{ entries: Pick<", "KnowledgeBaseEntry", - "[]; }>; } & ", + ", \"id\" | \"text\">[]; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", "; \"POST /internal/observability_ai_assistant/functions/elasticsearch\": { endpoint: \"POST /internal/observability_ai_assistant/functions/elasticsearch\"; params?: ", "TypeC", diff --git a/api_docs/observability_a_i_assistant.mdx b/api_docs/observability_a_i_assistant.mdx index 9b7af89b606e3..f2c1ad4099954 100644 --- a/api_docs/observability_a_i_assistant.mdx +++ b/api_docs/observability_a_i_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAIAssistant title: "observabilityAIAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAIAssistant plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAIAssistant'] --- import observabilityAIAssistantObj from './observability_a_i_assistant.devdocs.json'; diff --git a/api_docs/observability_onboarding.mdx b/api_docs/observability_onboarding.mdx index 5c1c30f656ff8..35299f4069006 100644 --- a/api_docs/observability_onboarding.mdx +++ b/api_docs/observability_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityOnboarding title: "observabilityOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityOnboarding plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityOnboarding'] --- import observabilityOnboardingObj from './observability_onboarding.devdocs.json'; diff --git a/api_docs/observability_shared.mdx b/api_docs/observability_shared.mdx index 383ed4d08c903..bde051c6aef8c 100644 --- a/api_docs/observability_shared.mdx +++ b/api_docs/observability_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityShared title: "observabilityShared" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityShared plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityShared'] --- import observabilitySharedObj from './observability_shared.devdocs.json'; diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index 679268d0a085d..8833f6a1ca67b 100644 --- a/api_docs/osquery.mdx +++ b/api_docs/osquery.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/osquery title: "osquery" image: https://source.unsplash.com/400x175/?github description: API docs for the osquery plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] --- import osqueryObj from './osquery.devdocs.json'; diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index ec5de1385c7d1..9bfdfb272f12a 100644 --- a/api_docs/plugin_directory.mdx +++ b/api_docs/plugin_directory.mdx @@ -7,7 +7,7 @@ id: kibDevDocsPluginDirectory slug: /kibana-dev-docs/api-meta/plugin-api-directory title: Directory description: Directory of public APIs available through plugins or packages. -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -15,13 +15,13 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Count | Plugins or Packages with a
public API | Number of teams | |--------------|----------|------------------------| -| 666 | 556 | 39 | +| 668 | 558 | 39 | ### Public API health stats | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 72265 | 227 | 61729 | 1491 | +| 72283 | 226 | 61740 | 1491 | ## Plugin Directory @@ -68,6 +68,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 37 | 0 | 35 | 2 | | discoverLogExplorer | [@elastic/infra-monitoring-ui](https://github.com/orgs/elastic/teams/infra-monitoring-ui) | This plugin exposes and registers Logs+ features. | 0 | 0 | 0 | 0 | | | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | APIs used to assess the quality of data in Elasticsearch indexes | 2 | 0 | 0 | 0 | +| | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | Server APIs for the Elastic AI Assistant | 4 | 0 | 2 | 0 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds embeddables service to Kibana | 542 | 1 | 442 | 7 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Extends embeddable plugin with more functionality | 14 | 0 | 14 | 0 | | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides encryption and decryption utilities for saved objects containing sensitive information. | 51 | 0 | 44 | 0 | @@ -106,7 +107,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 147 | 0 | 108 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Image embeddable | 3 | 0 | 3 | 1 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 4 | 0 | 4 | 0 | -| | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 182 | 0 | 177 | 4 | +| | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 191 | 0 | 186 | 4 | | | [@elastic/infra-monitoring-ui](https://github.com/orgs/elastic/teams/infra-monitoring-ui) | This plugin visualizes data from Filebeat and Metricbeat, and integrates with other Observability solutions | 45 | 0 | 42 | 11 | | ingestPipelines | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 0 | 0 | 0 | 0 | | inputControlVis | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds Input Control visualization to Kibana | 0 | 0 | 0 | 0 | @@ -132,6 +133,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/infra-monitoring-ui](https://github.com/orgs/elastic/teams/infra-monitoring-ui) | - | 9 | 0 | 9 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 34 | 0 | 34 | 2 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 17 | 0 | 17 | 0 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 3 | 0 | 3 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 2 | 0 | 2 | 1 | | | [@elastic/actionable-observability](https://github.com/orgs/elastic/teams/actionable-observability) | - | 541 | 2 | 532 | 16 | | | [@elastic/obs-ai-assistant](https://github.com/orgs/elastic/teams/obs-ai-assistant) | - | 42 | 0 | 39 | 7 | @@ -292,9 +294,9 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 4 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 22 | 0 | 13 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 37 | 1 | 33 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 108 | 0 | 54 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 37 | 0 | 33 | 3 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 15 | 1 | 15 | 0 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 111 | 0 | 54 | 0 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 38 | 0 | 34 | 3 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 13 | 1 | 13 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 4 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 4 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 10 | 0 | 2 | 1 | @@ -363,7 +365,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 4 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 25 | 1 | 24 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 111 | 1 | 0 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 351 | 1 | 7 | 1 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 351 | 1 | 5 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 11 | 0 | 11 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 89 | 0 | 61 | 10 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 4 | 0 | @@ -373,7 +375,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 73 | 0 | 40 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 26 | 0 | 23 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 4 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 124 | 0 | 90 | 46 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 125 | 0 | 91 | 46 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 12 | 0 | 12 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 548 | 1 | 122 | 4 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 69 | 0 | 69 | 4 | @@ -521,7 +523,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 16 | 0 | 16 | 1 | | | [@elastic/security-detections-response](https://github.com/orgs/elastic/teams/security-detections-response) | - | 107 | 0 | 104 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 2 | 0 | 2 | 0 | -| | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 64 | 1 | 64 | 0 | +| | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 64 | 0 | 64 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 16 | 0 | 8 | 0 | | | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 50 | 0 | 47 | 0 | | | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 29 | 0 | 23 | 0 | @@ -588,7 +590,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 2 | 0 | 0 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 15 | 0 | 4 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 9 | 0 | 3 | 0 | -| | [@elastic/actionable-observability](https://github.com/orgs/elastic/teams/actionable-observability) | - | 133 | 0 | 130 | 0 | +| | [@elastic/actionable-observability](https://github.com/orgs/elastic/teams/actionable-observability) | - | 132 | 0 | 129 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 20 | 0 | 12 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 102 | 2 | 65 | 1 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 4 | 0 | 2 | 0 | diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index 9e9d187e99f0c..cdc6fdb4be051 100644 --- a/api_docs/presentation_util.mdx +++ b/api_docs/presentation_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationUtil title: "presentationUtil" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationUtil plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationUtil'] --- import presentationUtilObj from './presentation_util.devdocs.json'; diff --git a/api_docs/profiling.mdx b/api_docs/profiling.mdx index 3661f0be02f85..b9e7c8fa13d56 100644 --- a/api_docs/profiling.mdx +++ b/api_docs/profiling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profiling title: "profiling" image: https://source.unsplash.com/400x175/?github description: API docs for the profiling plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profiling'] --- import profilingObj from './profiling.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index 79a18388225f3..a1f6c7a297e1a 100644 --- a/api_docs/remote_clusters.mdx +++ b/api_docs/remote_clusters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/remoteClusters title: "remoteClusters" image: https://source.unsplash.com/400x175/?github description: API docs for the remoteClusters plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'remoteClusters'] --- import remoteClustersObj from './remote_clusters.devdocs.json'; diff --git a/api_docs/reporting.mdx b/api_docs/reporting.mdx index a53247a25e951..8b4a86aeeb456 100644 --- a/api_docs/reporting.mdx +++ b/api_docs/reporting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/reporting title: "reporting" image: https://source.unsplash.com/400x175/?github description: API docs for the reporting plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'reporting'] --- import reportingObj from './reporting.devdocs.json'; diff --git a/api_docs/rollup.mdx b/api_docs/rollup.mdx index 22fd762ce0d5d..0b3598ae263ed 100644 --- a/api_docs/rollup.mdx +++ b/api_docs/rollup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/rollup title: "rollup" image: https://source.unsplash.com/400x175/?github description: API docs for the rollup plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'rollup'] --- import rollupObj from './rollup.devdocs.json'; diff --git a/api_docs/rule_registry.mdx b/api_docs/rule_registry.mdx index c4646d2838f4e..5922688ec3d5f 100644 --- a/api_docs/rule_registry.mdx +++ b/api_docs/rule_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ruleRegistry title: "ruleRegistry" image: https://source.unsplash.com/400x175/?github description: API docs for the ruleRegistry plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ruleRegistry'] --- import ruleRegistryObj from './rule_registry.devdocs.json'; diff --git a/api_docs/runtime_fields.mdx b/api_docs/runtime_fields.mdx index ad6badd31b87f..eafa39a8b1ec8 100644 --- a/api_docs/runtime_fields.mdx +++ b/api_docs/runtime_fields.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/runtimeFields title: "runtimeFields" image: https://source.unsplash.com/400x175/?github description: API docs for the runtimeFields plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'runtimeFields'] --- import runtimeFieldsObj from './runtime_fields.devdocs.json'; diff --git a/api_docs/saved_objects.mdx b/api_docs/saved_objects.mdx index 96c0e4200229c..1ba7eadf0ef98 100644 --- a/api_docs/saved_objects.mdx +++ b/api_docs/saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjects title: "savedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjects plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjects'] --- import savedObjectsObj from './saved_objects.devdocs.json'; diff --git a/api_docs/saved_objects_finder.mdx b/api_docs/saved_objects_finder.mdx index 62e42d48bb637..e23535dc14647 100644 --- a/api_docs/saved_objects_finder.mdx +++ b/api_docs/saved_objects_finder.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsFinder title: "savedObjectsFinder" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsFinder plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsFinder'] --- import savedObjectsFinderObj from './saved_objects_finder.devdocs.json'; diff --git a/api_docs/saved_objects_management.mdx b/api_docs/saved_objects_management.mdx index a5294ce5dc851..98c2613f77c9a 100644 --- a/api_docs/saved_objects_management.mdx +++ b/api_docs/saved_objects_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsManagement title: "savedObjectsManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsManagement plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsManagement'] --- import savedObjectsManagementObj from './saved_objects_management.devdocs.json'; diff --git a/api_docs/saved_objects_tagging.mdx b/api_docs/saved_objects_tagging.mdx index cd9f852e66e04..a063eb533b714 100644 --- a/api_docs/saved_objects_tagging.mdx +++ b/api_docs/saved_objects_tagging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTagging title: "savedObjectsTagging" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTagging plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTagging'] --- import savedObjectsTaggingObj from './saved_objects_tagging.devdocs.json'; diff --git a/api_docs/saved_objects_tagging_oss.mdx b/api_docs/saved_objects_tagging_oss.mdx index 935d782e0a290..fade576fdfed1 100644 --- a/api_docs/saved_objects_tagging_oss.mdx +++ b/api_docs/saved_objects_tagging_oss.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTaggingOss title: "savedObjectsTaggingOss" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTaggingOss plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTaggingOss'] --- import savedObjectsTaggingOssObj from './saved_objects_tagging_oss.devdocs.json'; diff --git a/api_docs/saved_search.mdx b/api_docs/saved_search.mdx index 17a5ac41bbb33..15e49ecd643ca 100644 --- a/api_docs/saved_search.mdx +++ b/api_docs/saved_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedSearch title: "savedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the savedSearch plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedSearch'] --- import savedSearchObj from './saved_search.devdocs.json'; diff --git a/api_docs/screenshot_mode.mdx b/api_docs/screenshot_mode.mdx index d1a17c8e99667..8e3054654149e 100644 --- a/api_docs/screenshot_mode.mdx +++ b/api_docs/screenshot_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotMode title: "screenshotMode" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotMode plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotMode'] --- import screenshotModeObj from './screenshot_mode.devdocs.json'; diff --git a/api_docs/screenshotting.mdx b/api_docs/screenshotting.mdx index 50e788fdbf4d8..a56afa3ea5eab 100644 --- a/api_docs/screenshotting.mdx +++ b/api_docs/screenshotting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotting title: "screenshotting" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotting plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/security.mdx b/api_docs/security.mdx index 0cf9f8f2a49c7..436d9fc2a62b3 100644 --- a/api_docs/security.mdx +++ b/api_docs/security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/security title: "security" image: https://source.unsplash.com/400x175/?github description: API docs for the security plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'security'] --- import securityObj from './security.devdocs.json'; diff --git a/api_docs/security_solution.devdocs.json b/api_docs/security_solution.devdocs.json index e9b4ad63731a2..5ebed17999232 100644 --- a/api_docs/security_solution.devdocs.json +++ b/api_docs/security_solution.devdocs.json @@ -101,7 +101,7 @@ "label": "experimentalFeatures", "description": [], "signature": [ - "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly alertsPreviewChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly alertDetailsPageEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly newUserDetailsFlyout: boolean; readonly detectionsCoverageOverview: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly discoverInTimeline: boolean; }" + "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly alertsPreviewChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly alertDetailsPageEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly newUserDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly discoverInTimeline: boolean; readonly protectionUpdatesEnabled: boolean; }" ], "path": "x-pack/plugins/security_solution/public/plugin.tsx", "deprecated": false, @@ -480,7 +480,7 @@ "\nExperimental flag needed to enable the link" ], "signature": [ - "\"tGridEnabled\" | \"tGridEventRenderedViewEnabled\" | \"excludePoliciesInFilterEnabled\" | \"kubernetesEnabled\" | \"chartEmbeddablesEnabled\" | \"donutChartEmbeddablesEnabled\" | \"alertsPreviewChartEmbeddablesEnabled\" | \"previewTelemetryUrlEnabled\" | \"insightsRelatedAlertsByProcessAncestry\" | \"extendedRuleExecutionLoggingEnabled\" | \"socTrendsEnabled\" | \"responseActionsEnabled\" | \"endpointResponseActionsEnabled\" | \"alertDetailsPageEnabled\" | \"responseActionUploadEnabled\" | \"alertsPageChartsEnabled\" | \"alertTypeEnabled\" | \"alertsPageFiltersEnabled\" | \"newUserDetailsFlyout\" | \"detectionsCoverageOverview\" | \"riskScoringPersistence\" | \"riskScoringRoutesEnabled\" | \"discoverInTimeline\" | undefined" + "\"tGridEnabled\" | \"tGridEventRenderedViewEnabled\" | \"excludePoliciesInFilterEnabled\" | \"kubernetesEnabled\" | \"chartEmbeddablesEnabled\" | \"donutChartEmbeddablesEnabled\" | \"alertsPreviewChartEmbeddablesEnabled\" | \"previewTelemetryUrlEnabled\" | \"insightsRelatedAlertsByProcessAncestry\" | \"extendedRuleExecutionLoggingEnabled\" | \"socTrendsEnabled\" | \"responseActionsEnabled\" | \"endpointResponseActionsEnabled\" | \"alertDetailsPageEnabled\" | \"responseActionUploadEnabled\" | \"alertsPageChartsEnabled\" | \"alertTypeEnabled\" | \"alertsPageFiltersEnabled\" | \"newUserDetailsFlyout\" | \"riskScoringPersistence\" | \"riskScoringRoutesEnabled\" | \"discoverInTimeline\" | \"protectionUpdatesEnabled\" | undefined" ], "path": "x-pack/plugins/security_solution/public/common/links/types.ts", "deprecated": false, @@ -560,7 +560,7 @@ "\nExperimental flag needed to disable the link. Opposite of experimentalKey" ], "signature": [ - "\"tGridEnabled\" | \"tGridEventRenderedViewEnabled\" | \"excludePoliciesInFilterEnabled\" | \"kubernetesEnabled\" | \"chartEmbeddablesEnabled\" | \"donutChartEmbeddablesEnabled\" | \"alertsPreviewChartEmbeddablesEnabled\" | \"previewTelemetryUrlEnabled\" | \"insightsRelatedAlertsByProcessAncestry\" | \"extendedRuleExecutionLoggingEnabled\" | \"socTrendsEnabled\" | \"responseActionsEnabled\" | \"endpointResponseActionsEnabled\" | \"alertDetailsPageEnabled\" | \"responseActionUploadEnabled\" | \"alertsPageChartsEnabled\" | \"alertTypeEnabled\" | \"alertsPageFiltersEnabled\" | \"newUserDetailsFlyout\" | \"detectionsCoverageOverview\" | \"riskScoringPersistence\" | \"riskScoringRoutesEnabled\" | \"discoverInTimeline\" | undefined" + "\"tGridEnabled\" | \"tGridEventRenderedViewEnabled\" | \"excludePoliciesInFilterEnabled\" | \"kubernetesEnabled\" | \"chartEmbeddablesEnabled\" | \"donutChartEmbeddablesEnabled\" | \"alertsPreviewChartEmbeddablesEnabled\" | \"previewTelemetryUrlEnabled\" | \"insightsRelatedAlertsByProcessAncestry\" | \"extendedRuleExecutionLoggingEnabled\" | \"socTrendsEnabled\" | \"responseActionsEnabled\" | \"endpointResponseActionsEnabled\" | \"alertDetailsPageEnabled\" | \"responseActionUploadEnabled\" | \"alertsPageChartsEnabled\" | \"alertTypeEnabled\" | \"alertsPageFiltersEnabled\" | \"newUserDetailsFlyout\" | \"riskScoringPersistence\" | \"riskScoringRoutesEnabled\" | \"discoverInTimeline\" | \"protectionUpdatesEnabled\" | undefined" ], "path": "x-pack/plugins/security_solution/public/common/links/types.ts", "deprecated": false, @@ -3019,7 +3019,7 @@ "label": "ExperimentalFeatures", "description": [], "signature": [ - "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly alertsPreviewChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly alertDetailsPageEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly newUserDetailsFlyout: boolean; readonly detectionsCoverageOverview: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly discoverInTimeline: boolean; }" + "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly alertsPreviewChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly alertDetailsPageEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly newUserDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly discoverInTimeline: boolean; readonly protectionUpdatesEnabled: boolean; }" ], "path": "x-pack/plugins/security_solution/common/experimental_features.ts", "deprecated": false, @@ -3089,7 +3089,7 @@ "\nA list of allowed values that can be used in `xpack.securitySolution.enableExperimental`.\nThis object is then used to validate and parse the value entered." ], "signature": [ - "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly alertsPreviewChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly alertDetailsPageEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly newUserDetailsFlyout: boolean; readonly detectionsCoverageOverview: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly discoverInTimeline: boolean; }" + "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly alertsPreviewChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly alertDetailsPageEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly newUserDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly discoverInTimeline: boolean; readonly protectionUpdatesEnabled: boolean; }" ], "path": "x-pack/plugins/security_solution/common/experimental_features.ts", "deprecated": false, diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index 07327a83ece61..41c7553e1292c 100644 --- a/api_docs/security_solution.mdx +++ b/api_docs/security_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolution title: "securitySolution" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolution plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; diff --git a/api_docs/security_solution_ess.mdx b/api_docs/security_solution_ess.mdx index 4611939fe8167..34e38e14067dd 100644 --- a/api_docs/security_solution_ess.mdx +++ b/api_docs/security_solution_ess.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionEss title: "securitySolutionEss" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionEss plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolutionEss'] --- import securitySolutionEssObj from './security_solution_ess.devdocs.json'; diff --git a/api_docs/security_solution_serverless.mdx b/api_docs/security_solution_serverless.mdx index b48883227f8e6..e5e61401fc375 100644 --- a/api_docs/security_solution_serverless.mdx +++ b/api_docs/security_solution_serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionServerless title: "securitySolutionServerless" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionServerless plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolutionServerless'] --- import securitySolutionServerlessObj from './security_solution_serverless.devdocs.json'; diff --git a/api_docs/serverless.mdx b/api_docs/serverless.mdx index 8b33bc3230e99..cd2b74a76fdf9 100644 --- a/api_docs/serverless.mdx +++ b/api_docs/serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverless title: "serverless" image: https://source.unsplash.com/400x175/?github description: API docs for the serverless plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverless'] --- import serverlessObj from './serverless.devdocs.json'; diff --git a/api_docs/serverless_observability.mdx b/api_docs/serverless_observability.mdx index d7daf23a20a6f..651a305fafd4c 100644 --- a/api_docs/serverless_observability.mdx +++ b/api_docs/serverless_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessObservability title: "serverlessObservability" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessObservability plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverlessObservability'] --- import serverlessObservabilityObj from './serverless_observability.devdocs.json'; diff --git a/api_docs/serverless_search.mdx b/api_docs/serverless_search.mdx index 9a3d8b1b2ab14..f02a9caaafbe1 100644 --- a/api_docs/serverless_search.mdx +++ b/api_docs/serverless_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessSearch title: "serverlessSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessSearch plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverlessSearch'] --- import serverlessSearchObj from './serverless_search.devdocs.json'; diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index aa8fb52c9d3b0..b9480241e7352 100644 --- a/api_docs/session_view.mdx +++ b/api_docs/session_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/sessionView title: "sessionView" image: https://source.unsplash.com/400x175/?github description: API docs for the sessionView plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'sessionView'] --- import sessionViewObj from './session_view.devdocs.json'; diff --git a/api_docs/share.mdx b/api_docs/share.mdx index ac0cccd2adfce..1ee73c2a226e6 100644 --- a/api_docs/share.mdx +++ b/api_docs/share.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/share title: "share" image: https://source.unsplash.com/400x175/?github description: API docs for the share plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] --- import shareObj from './share.devdocs.json'; diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx index a481bc695f2ad..c8ca4eca7cce4 100644 --- a/api_docs/snapshot_restore.mdx +++ b/api_docs/snapshot_restore.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/snapshotRestore title: "snapshotRestore" image: https://source.unsplash.com/400x175/?github description: API docs for the snapshotRestore plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'snapshotRestore'] --- import snapshotRestoreObj from './snapshot_restore.devdocs.json'; diff --git a/api_docs/spaces.mdx b/api_docs/spaces.mdx index 0ebfed1192228..deb0f1803fa38 100644 --- a/api_docs/spaces.mdx +++ b/api_docs/spaces.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/spaces title: "spaces" image: https://source.unsplash.com/400x175/?github description: API docs for the spaces plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'spaces'] --- import spacesObj from './spaces.devdocs.json'; diff --git a/api_docs/stack_alerts.mdx b/api_docs/stack_alerts.mdx index 8dfc7a76db728..6f64356d89cea 100644 --- a/api_docs/stack_alerts.mdx +++ b/api_docs/stack_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackAlerts title: "stackAlerts" image: https://source.unsplash.com/400x175/?github description: API docs for the stackAlerts plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackAlerts'] --- import stackAlertsObj from './stack_alerts.devdocs.json'; diff --git a/api_docs/stack_connectors.mdx b/api_docs/stack_connectors.mdx index 167c3f2ae6129..b9dcc1d398047 100644 --- a/api_docs/stack_connectors.mdx +++ b/api_docs/stack_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackConnectors title: "stackConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the stackConnectors plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackConnectors'] --- import stackConnectorsObj from './stack_connectors.devdocs.json'; diff --git a/api_docs/task_manager.mdx b/api_docs/task_manager.mdx index 688089acc6d5f..892313b66db13 100644 --- a/api_docs/task_manager.mdx +++ b/api_docs/task_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/taskManager title: "taskManager" image: https://source.unsplash.com/400x175/?github description: API docs for the taskManager plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'taskManager'] --- import taskManagerObj from './task_manager.devdocs.json'; diff --git a/api_docs/telemetry.mdx b/api_docs/telemetry.mdx index da7dcbe2546ec..9026cd46b9b65 100644 --- a/api_docs/telemetry.mdx +++ b/api_docs/telemetry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetry title: "telemetry" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetry plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetry'] --- import telemetryObj from './telemetry.devdocs.json'; diff --git a/api_docs/telemetry_collection_manager.mdx b/api_docs/telemetry_collection_manager.mdx index a171af464f0e4..d73c7d6246bbd 100644 --- a/api_docs/telemetry_collection_manager.mdx +++ b/api_docs/telemetry_collection_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionManager title: "telemetryCollectionManager" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionManager plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionManager'] --- import telemetryCollectionManagerObj from './telemetry_collection_manager.devdocs.json'; diff --git a/api_docs/telemetry_collection_xpack.mdx b/api_docs/telemetry_collection_xpack.mdx index 8898b48a648fe..5e0a86eb22bfb 100644 --- a/api_docs/telemetry_collection_xpack.mdx +++ b/api_docs/telemetry_collection_xpack.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionXpack title: "telemetryCollectionXpack" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionXpack plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionXpack'] --- import telemetryCollectionXpackObj from './telemetry_collection_xpack.devdocs.json'; diff --git a/api_docs/telemetry_management_section.mdx b/api_docs/telemetry_management_section.mdx index 72bd0937044b8..000818037c4e2 100644 --- a/api_docs/telemetry_management_section.mdx +++ b/api_docs/telemetry_management_section.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryManagementSection title: "telemetryManagementSection" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryManagementSection plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryManagementSection'] --- import telemetryManagementSectionObj from './telemetry_management_section.devdocs.json'; diff --git a/api_docs/text_based_languages.mdx b/api_docs/text_based_languages.mdx index 59807b0f6bfa6..d59e94ecd47ed 100644 --- a/api_docs/text_based_languages.mdx +++ b/api_docs/text_based_languages.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/textBasedLanguages title: "textBasedLanguages" image: https://source.unsplash.com/400x175/?github description: API docs for the textBasedLanguages plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'textBasedLanguages'] --- import textBasedLanguagesObj from './text_based_languages.devdocs.json'; diff --git a/api_docs/threat_intelligence.mdx b/api_docs/threat_intelligence.mdx index 97fbd6d87a35d..f94df2ae0551f 100644 --- a/api_docs/threat_intelligence.mdx +++ b/api_docs/threat_intelligence.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/threatIntelligence title: "threatIntelligence" image: https://source.unsplash.com/400x175/?github description: API docs for the threatIntelligence plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'threatIntelligence'] --- import threatIntelligenceObj from './threat_intelligence.devdocs.json'; diff --git a/api_docs/timelines.devdocs.json b/api_docs/timelines.devdocs.json index bca1001561f09..a6c77777588d2 100644 --- a/api_docs/timelines.devdocs.json +++ b/api_docs/timelines.devdocs.json @@ -4591,119 +4591,119 @@ }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx" + "path": "x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx" + "path": "x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx" + "path": "x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/add_data_provider_popover.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx" + "path": "x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/add_data_provider_popover.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx" + "path": "x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/empty.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/components/event_details/overview/index.tsx" + "path": "x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/empty.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/components/event_details/overview/index.tsx" + "path": "x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_source_event.tsx" + "path": "x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_source_event.tsx" + "path": "x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_session.tsx" + "path": "x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_session.tsx" + "path": "x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/components/event_details/insights/insights.tsx" + "path": "x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/components/event_details/insights/insights.tsx" + "path": "x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx" + "path": "x-pack/plugins/security_solution/public/common/components/event_details/overview/index.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx" + "path": "x-pack/plugins/security_solution/public/common/components/event_details/overview/index.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/expandable_event.tsx" + "path": "x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_source_event.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/expandable_event.tsx" + "path": "x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_source_event.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx" + "path": "x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_session.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx" + "path": "x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_session.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/timelines/components/side_panel/index.tsx" + "path": "x-pack/plugins/security_solution/public/common/components/event_details/insights/insights.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/timelines/components/side_panel/index.tsx" + "path": "x-pack/plugins/security_solution/public/common/components/event_details/insights/insights.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.tsx" + "path": "x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.tsx" + "path": "x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/add_data_provider_popover.tsx" + "path": "x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/expandable_event.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/add_data_provider_popover.tsx" + "path": "x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/expandable_event.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/empty.tsx" + "path": "x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/empty.tsx" + "path": "x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx" + "path": "x-pack/plugins/security_solution/public/timelines/components/side_panel/index.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx" + "path": "x-pack/plugins/security_solution/public/timelines/components/side_panel/index.tsx" }, { "plugin": "securitySolution", @@ -4727,27 +4727,27 @@ }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx" + "path": "x-pack/plugins/security_solution/public/flyout/right/context.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx" + "path": "x-pack/plugins/security_solution/public/flyout/right/context.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/flyout/right/context.tsx" + "path": "x-pack/plugins/security_solution/public/flyout/left/context.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/flyout/right/context.tsx" + "path": "x-pack/plugins/security_solution/public/flyout/left/context.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/flyout/left/context.tsx" + "path": "x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/flyout/left/context.tsx" + "path": "x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx" }, { "plugin": "@kbn/securitysolution-data-table", diff --git a/api_docs/timelines.mdx b/api_docs/timelines.mdx index c8dcc87ed8e56..db9408698ada4 100644 --- a/api_docs/timelines.mdx +++ b/api_docs/timelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/timelines title: "timelines" image: https://source.unsplash.com/400x175/?github description: API docs for the timelines plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'timelines'] --- import timelinesObj from './timelines.devdocs.json'; diff --git a/api_docs/transform.mdx b/api_docs/transform.mdx index a8a64b67f98cf..c5c72936b736f 100644 --- a/api_docs/transform.mdx +++ b/api_docs/transform.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/transform title: "transform" image: https://source.unsplash.com/400x175/?github description: API docs for the transform plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'transform'] --- import transformObj from './transform.devdocs.json'; diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx index 7d45856119d21..a87bb62ead45a 100644 --- a/api_docs/triggers_actions_ui.mdx +++ b/api_docs/triggers_actions_ui.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/triggersActionsUi title: "triggersActionsUi" image: https://source.unsplash.com/400x175/?github description: API docs for the triggersActionsUi plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'triggersActionsUi'] --- import triggersActionsUiObj from './triggers_actions_ui.devdocs.json'; diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx index 829f7ce6cb4e9..2918437a402fa 100644 --- a/api_docs/ui_actions.mdx +++ b/api_docs/ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActions title: "uiActions" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActions plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActions'] --- import uiActionsObj from './ui_actions.devdocs.json'; diff --git a/api_docs/ui_actions_enhanced.mdx b/api_docs/ui_actions_enhanced.mdx index a4bcd288b21ae..e3f159118cf9c 100644 --- a/api_docs/ui_actions_enhanced.mdx +++ b/api_docs/ui_actions_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActionsEnhanced title: "uiActionsEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActionsEnhanced plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_histogram.mdx b/api_docs/unified_histogram.mdx index 4c27a5fd878ea..50929095b7b86 100644 --- a/api_docs/unified_histogram.mdx +++ b/api_docs/unified_histogram.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedHistogram title: "unifiedHistogram" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedHistogram plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedHistogram'] --- import unifiedHistogramObj from './unified_histogram.devdocs.json'; diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index c7f9c3fc72779..2af9c4d1aee5a 100644 --- a/api_docs/unified_search.mdx +++ b/api_docs/unified_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch title: "unifiedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch'] --- import unifiedSearchObj from './unified_search.devdocs.json'; diff --git a/api_docs/unified_search_autocomplete.mdx b/api_docs/unified_search_autocomplete.mdx index 126d2216474a3..abbd20438c48c 100644 --- a/api_docs/unified_search_autocomplete.mdx +++ b/api_docs/unified_search_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch-autocomplete title: "unifiedSearch.autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch.autocomplete plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] --- import unifiedSearchAutocompleteObj from './unified_search_autocomplete.devdocs.json'; diff --git a/api_docs/uptime.mdx b/api_docs/uptime.mdx index cd213fb77faef..2c463d45f48b3 100644 --- a/api_docs/uptime.mdx +++ b/api_docs/uptime.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uptime title: "uptime" image: https://source.unsplash.com/400x175/?github description: API docs for the uptime plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uptime'] --- import uptimeObj from './uptime.devdocs.json'; diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index ab1ac62bd115b..595e139f1e17c 100644 --- a/api_docs/url_forwarding.mdx +++ b/api_docs/url_forwarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/urlForwarding title: "urlForwarding" image: https://source.unsplash.com/400x175/?github description: API docs for the urlForwarding plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'urlForwarding'] --- import urlForwardingObj from './url_forwarding.devdocs.json'; diff --git a/api_docs/usage_collection.mdx b/api_docs/usage_collection.mdx index 34bda30635762..91bde275ef5b2 100644 --- a/api_docs/usage_collection.mdx +++ b/api_docs/usage_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/usageCollection title: "usageCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the usageCollection plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'usageCollection'] --- import usageCollectionObj from './usage_collection.devdocs.json'; diff --git a/api_docs/ux.mdx b/api_docs/ux.mdx index 5387d08ff66fa..bc82604a8e09d 100644 --- a/api_docs/ux.mdx +++ b/api_docs/ux.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ux title: "ux" image: https://source.unsplash.com/400x175/?github description: API docs for the ux plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ux'] --- import uxObj from './ux.devdocs.json'; diff --git a/api_docs/vis_default_editor.mdx b/api_docs/vis_default_editor.mdx index af898492cb68e..d789cfa9d6ddd 100644 --- a/api_docs/vis_default_editor.mdx +++ b/api_docs/vis_default_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visDefaultEditor title: "visDefaultEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the visDefaultEditor plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visDefaultEditor'] --- import visDefaultEditorObj from './vis_default_editor.devdocs.json'; diff --git a/api_docs/vis_type_gauge.mdx b/api_docs/vis_type_gauge.mdx index 846b481b72a60..b7fbc17a2559b 100644 --- a/api_docs/vis_type_gauge.mdx +++ b/api_docs/vis_type_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeGauge title: "visTypeGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeGauge plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeGauge'] --- import visTypeGaugeObj from './vis_type_gauge.devdocs.json'; diff --git a/api_docs/vis_type_heatmap.mdx b/api_docs/vis_type_heatmap.mdx index 48552ad33a02a..e58463315b9c5 100644 --- a/api_docs/vis_type_heatmap.mdx +++ b/api_docs/vis_type_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeHeatmap title: "visTypeHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeHeatmap plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeHeatmap'] --- import visTypeHeatmapObj from './vis_type_heatmap.devdocs.json'; diff --git a/api_docs/vis_type_pie.mdx b/api_docs/vis_type_pie.mdx index 7d75a8bea6854..6501a0fb4648e 100644 --- a/api_docs/vis_type_pie.mdx +++ b/api_docs/vis_type_pie.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypePie title: "visTypePie" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypePie plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypePie'] --- import visTypePieObj from './vis_type_pie.devdocs.json'; diff --git a/api_docs/vis_type_table.mdx b/api_docs/vis_type_table.mdx index 887c7162168bf..d4313e88fa7ac 100644 --- a/api_docs/vis_type_table.mdx +++ b/api_docs/vis_type_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTable title: "visTypeTable" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTable plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTable'] --- import visTypeTableObj from './vis_type_table.devdocs.json'; diff --git a/api_docs/vis_type_timelion.mdx b/api_docs/vis_type_timelion.mdx index 18b79e475f91e..f59755691b08d 100644 --- a/api_docs/vis_type_timelion.mdx +++ b/api_docs/vis_type_timelion.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimelion title: "visTypeTimelion" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimelion plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimelion'] --- import visTypeTimelionObj from './vis_type_timelion.devdocs.json'; diff --git a/api_docs/vis_type_timeseries.mdx b/api_docs/vis_type_timeseries.mdx index 568224480bbf6..05e3730a4d31a 100644 --- a/api_docs/vis_type_timeseries.mdx +++ b/api_docs/vis_type_timeseries.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimeseries title: "visTypeTimeseries" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimeseries plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimeseries'] --- import visTypeTimeseriesObj from './vis_type_timeseries.devdocs.json'; diff --git a/api_docs/vis_type_vega.mdx b/api_docs/vis_type_vega.mdx index 17df71793f6a8..b103c87abbb3c 100644 --- a/api_docs/vis_type_vega.mdx +++ b/api_docs/vis_type_vega.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVega title: "visTypeVega" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVega plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVega'] --- import visTypeVegaObj from './vis_type_vega.devdocs.json'; diff --git a/api_docs/vis_type_vislib.mdx b/api_docs/vis_type_vislib.mdx index 4d3ba5f0554cc..0b3e8fc1048ed 100644 --- a/api_docs/vis_type_vislib.mdx +++ b/api_docs/vis_type_vislib.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVislib title: "visTypeVislib" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVislib plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVislib'] --- import visTypeVislibObj from './vis_type_vislib.devdocs.json'; diff --git a/api_docs/vis_type_xy.mdx b/api_docs/vis_type_xy.mdx index ee2f8ed47c901..420f95794b21e 100644 --- a/api_docs/vis_type_xy.mdx +++ b/api_docs/vis_type_xy.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeXy title: "visTypeXy" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeXy plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] --- import visTypeXyObj from './vis_type_xy.devdocs.json'; diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index 7459f49a6d2ff..2f2b9042c4947 100644 --- a/api_docs/visualizations.mdx +++ b/api_docs/visualizations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visualizations title: "visualizations" image: https://source.unsplash.com/400x175/?github description: API docs for the visualizations plugin -date: 2023-08-28 +date: 2023-08-29 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; diff --git a/config/serverless.es.yml b/config/serverless.es.yml index 8ee07c718cce0..7a1da782855bd 100644 --- a/config/serverless.es.yml +++ b/config/serverless.es.yml @@ -32,3 +32,6 @@ telemetry.labels.serverless: search # Alerts config xpack.actions.enabledActionTypes: ['.email', '.index', '.slack', '.jira', '.webhook', '.teams'] + +# Customize empty page state for analytics apps +no_data_page.analyticsNoDataPageFlavor: 'serverless_search' diff --git a/config/serverless.yml b/config/serverless.yml index e18049e8517c6..c76e622d67547 100644 --- a/config/serverless.yml +++ b/config/serverless.yml @@ -13,6 +13,10 @@ xpack.cloud.base_url: "https://cloud.elastic.co" # Enable ZDT migration algorithm migrations.algorithm: zdt +# Limit batch size to reduce possibility of failures. +# A longer migration time is acceptable due to the ZDT algorithm. +migrations.batchSize: 250 + # temporarily allow to run the migration on UI nodes # until the controller is able to spawn the migrator job/pod migrations.zdt: @@ -86,6 +90,9 @@ console.autocompleteDefinitions.endpointsAvailability: serverless # Allow authentication via the Elasticsearch JWT realm with the `shared_secret` client authentication type. elasticsearch.requestHeadersWhitelist: ["authorization", "es-client-authentication"] +# Limit maxSockets to 800 as we do in ESS, which improves reliability under high loads. +elasticsearch.maxSockets: 800 + # Visualizations editors readonly settings vis_type_gauge.readOnly: true vis_type_heatmap.readOnly: true diff --git a/docs/api-generated/rules/rule-apis-passthru.asciidoc b/docs/api-generated/rules/rule-apis-passthru.asciidoc index 63aad889c865c..843f073f95e04 100644 --- a/docs/api-generated/rules/rule-apis-passthru.asciidoc +++ b/docs/api-generated/rules/rule-apis-passthru.asciidoc @@ -45,6 +45,7 @@ Any modifications made to this file will be overwritten.
  • post /s/{spaceId}/api/alerting/rule/{ruleId}/alert/{alertId}/_unmute
  • post /s/{spaceId}/api/alerting/rule/{ruleId}/_unmute_all
  • put /s/{spaceId}/api/alerting/rule/{ruleId}
  • +
  • post /s/{spaceId}/api/alerting/rule/{ruleId}/_update_api_key
  • Alerting

    @@ -2827,12 +2828,61 @@ Any modifications made to this file will be overwritten. 404_response
    +
    +
    + Up +
    post /s/{spaceId}/api/alerting/rule/{ruleId}/_update_api_key
    +
    Updates the API key for a rule. (updateRuleAPIKey)
    +
    The new API key has the credentials of the user that submits the request.
    + +

    Path parameters

    +
    +
    ruleId (required)
    + +
    Path Parameter — An identifier for the rule. default: null
    spaceId (required)
    + +
    Path Parameter — An identifier for the space. If /s/ and the identifier are omitted from the path, the default space is used. default: null
    +
    + + + +

    Request headers

    +
    +
    kbn-xsrf (required)
    + +
    Header Parameter — Cross-site request forgery protection default: null
    + +
    + + + + + + + +

    Produces

    + This API call produces the following media types according to the Accept request header; + the media type will be conveyed by the Content-Type response header. +
      +
    • application/json
    • +
    + +

    Responses

    +

    200

    + Indicates a successful call. + +

    400

    + Bad request + 400_response +
    +

    Models

    [ Jump to Methods ]

    Table of Contents

      +
    1. 400_response - Bad request
    2. 401_response - Unsuccessful rule API response
    3. 404_response -
    4. Count - Count
    5. @@ -2969,6 +3019,19 @@ Any modifications made to this file will be overwritten.
    6. update_rule_request - Update rule request
    +
    +

    400_response - Bad request Up

    +
    +
    +
    error
    +
    Enum:
    +
    Bad Request
    +
    message
    +
    statusCode
    +
    Enum:
    +
    400
    +
    +

    401_response - Unsuccessful rule API response Up

    diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index e8c008af6e0e8..632bce5883c28 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -254,6 +254,10 @@ It also provides a stateful version of it on the start contract. Content is fetched from the remote (https://feeds.elastic.co) once a day, with periodic checks if the content needs to be refreshed. All newsfeed content is hosted remotely. +|{kib-repo}blob/{branch}/src/plugins/no_data_page/README.md[noDataPage] +|Helps to globally configure the no data page components + + |{kib-repo}blob/{branch}/src/plugins/presentation_util/README.mdx[presentationUtil] |The Presentation Utility Plugin is a set of common, shared components and toolkits for solutions within the Presentation space, (e.g. Dashboards, Canvas). @@ -524,6 +528,10 @@ Plugin server-side only. Plugin has three main functions: |This plugin implements (server) APIs used to render the content of the Data Quality dashboard. +|{kib-repo}blob/{branch}/x-pack/plugins/elastic_assistant/README.md[elasticAssistant] +|This plugin implements (only) server APIs for the Elastic AI Assistant. + + |<> |Enhances Embeddables by registering a custom factory provider. The enhanced factory provider adds dynamic actions to every embeddables state, in order to support drilldowns. diff --git a/docs/user/security/securing-kibana.asciidoc b/docs/user/security/securing-kibana.asciidoc index 875830ab88b46..98290bb093e41 100644 --- a/docs/user/security/securing-kibana.asciidoc +++ b/docs/user/security/securing-kibana.asciidoc @@ -27,7 +27,7 @@ configure additional security settings and authentication. . Set the `xpack.security.encryptionKey` property in the `kibana.yml` configuration file. You can use any text string that is 32 characters or longer -as the encryption key. +as the encryption key. Refer to <>. + -- [source,yaml] @@ -35,7 +35,10 @@ as the encryption key. xpack.security.encryptionKey: "something_at_least_32_characters" ---- -For more information, see <>. +{kib}'s reporting and saved objects features also have encryption key settings. +Refer to <> and +<> +respectively. -- . Optional: <>. diff --git a/examples/search_examples/public/search/app.tsx b/examples/search_examples/public/search/app.tsx index e6aeeaae648bd..0fa13df35a4e9 100644 --- a/examples/search_examples/public/search/app.tsx +++ b/examples/search_examples/public/search/app.tsx @@ -30,7 +30,6 @@ import { DataPublicPluginStart, IKibanaSearchResponse, isCompleteResponse, - isErrorResponse, } from '@kbn/data-plugin/public'; import { SearchResponseWarning } from '@kbn/data-plugin/public/search/types'; import type { DataView, DataViewField } from '@kbn/data-views-plugin/public'; @@ -247,9 +246,6 @@ export const SearchExamplesApp = ({ text: toMountPoint(res.warning), }); } - } else if (isErrorResponse(res)) { - // TODO: Make response error status clearer - notifications.toasts.addDanger('An error has occurred'); } }, error: (e) => { @@ -401,10 +397,6 @@ export const SearchExamplesApp = ({ title: 'Query result', text: 'Query finished', }); - } else if (isErrorResponse(res)) { - setIsLoading(false); - // TODO: Make response error status clearer - notifications.toasts.addWarning('An error has occurred'); } }, error: (e) => { diff --git a/examples/search_examples/public/search_sessions/app.tsx b/examples/search_examples/public/search_sessions/app.tsx index 701665fd52075..8b7c7bbcaa144 100644 --- a/examples/search_examples/public/search_sessions/app.tsx +++ b/examples/search_examples/public/search_sessions/app.tsx @@ -40,7 +40,6 @@ import { IEsSearchRequest, IEsSearchResponse, isCompleteResponse, - isErrorResponse, QueryState, SearchSessionState, } from '@kbn/data-plugin/public'; @@ -724,8 +723,6 @@ function doSearch( title: 'Query result', text: mountReactNode(message), }); - } else if (isErrorResponse(res)) { - notifications.toasts.addWarning('An error has occurred'); } }), map((res) => ({ response: res, request: req, tookMs: performance.now() - startTs })), diff --git a/examples/search_examples/public/sql_search/app.tsx b/examples/search_examples/public/sql_search/app.tsx index f8278736e4a70..33406a53f9674 100644 --- a/examples/search_examples/public/sql_search/app.tsx +++ b/examples/search_examples/public/sql_search/app.tsx @@ -27,7 +27,6 @@ import { DataPublicPluginStart, IKibanaSearchResponse, isCompleteResponse, - isErrorResponse, } from '@kbn/data-plugin/public'; import { SQL_SEARCH_STRATEGY, @@ -70,10 +69,6 @@ export const SqlSearchExampleApp = ({ notifications, data }: SearchExamplesAppDe if (isCompleteResponse(res)) { setIsLoading(false); setResponse(res); - } else if (isErrorResponse(res)) { - setIsLoading(false); - setResponse(res); - notifications.toasts.addDanger('An error has occurred'); } }, error: (e) => { diff --git a/fleet_packages.json b/fleet_packages.json index 3570b649fb2d6..a7278bbdd809e 100644 --- a/fleet_packages.json +++ b/fleet_packages.json @@ -24,7 +24,7 @@ [ { "name": "apm", - "version": "8.11.0-preview-1692980426", + "version": "8.11.0-preview-1693211748", "forceAlignStackVersion": true, "allowSyncToPrerelease": true }, diff --git a/package.json b/package.json index a1c0e7d000ed9..fc076f17b7407 100644 --- a/package.json +++ b/package.json @@ -383,6 +383,7 @@ "@kbn/ecs-data-quality-dashboard": "link:x-pack/packages/security-solution/ecs_data_quality_dashboard", "@kbn/ecs-data-quality-dashboard-plugin": "link:x-pack/plugins/ecs_data_quality_dashboard", "@kbn/elastic-assistant": "link:x-pack/packages/kbn-elastic-assistant", + "@kbn/elastic-assistant-plugin": "link:x-pack/plugins/elastic_assistant", "@kbn/elasticsearch-client-plugin": "link:test/plugin_functional/plugins/elasticsearch_client_plugin", "@kbn/elasticsearch-client-xpack-plugin": "link:x-pack/test/plugin_api_integration/plugins/elasticsearch_client", "@kbn/embeddable-enhanced-plugin": "link:x-pack/plugins/embeddable_enhanced", @@ -535,6 +536,7 @@ "@kbn/navigation-plugin": "link:src/plugins/navigation", "@kbn/newsfeed-plugin": "link:src/plugins/newsfeed", "@kbn/newsfeed-test-plugin": "link:test/common/plugins/newsfeed", + "@kbn/no-data-page-plugin": "link:src/plugins/no_data_page", "@kbn/notifications-plugin": "link:x-pack/plugins/notifications", "@kbn/object-versioning": "link:packages/kbn-object-versioning", "@kbn/observability-ai-assistant-plugin": "link:x-pack/plugins/observability_ai_assistant", @@ -896,6 +898,7 @@ "jsonwebtoken": "^9.0.0", "jsts": "^1.6.2", "kea": "^2.4.2", + "langchain": "^0.0.132", "launchdarkly-js-client-sdk": "^2.22.1", "launchdarkly-node-server-sdk": "^6.4.2", "load-json-file": "^6.2.0", @@ -1558,6 +1561,7 @@ "val-loader": "^1.1.1", "vinyl-fs": "^4.0.0", "watchpack": "^1.6.0", + "web-streams-polyfill": "^3.2.1", "webpack": "^4.41.5", "webpack-bundle-analyzer": "^4.5.0", "webpack-cli": "^4.10.0", diff --git a/packages/core/http/core-http-server-internal/src/http_server.ts b/packages/core/http/core-http-server-internal/src/http_server.ts index 78cab6c6ea076..f12367419341f 100644 --- a/packages/core/http/core-http-server-internal/src/http_server.ts +++ b/packages/core/http/core-http-server-internal/src/http_server.ts @@ -399,8 +399,10 @@ export class HttpServer { const log = this.logger.get('http', 'server', 'response'); this.handleServerResponseEvent = (request) => { - const { message, meta } = getEcsResponseLog(request, this.log); - log.debug(message!, meta); + if (log.isLevelEnabled('debug')) { + const { message, meta } = getEcsResponseLog(request, this.log); + log.debug(message!, meta); + } }; this.server.events.on('response', this.handleServerResponseEvent); diff --git a/packages/core/saved-objects/core-saved-objects-api-browser/src/apis/find.ts b/packages/core/saved-objects/core-saved-objects-api-browser/src/apis/find.ts index 7dc54a9f6404b..8b2b154cb0a71 100644 --- a/packages/core/saved-objects/core-saved-objects-api-browser/src/apis/find.ts +++ b/packages/core/saved-objects/core-saved-objects-api-browser/src/apis/find.ts @@ -25,7 +25,6 @@ export type SavedObjectsFindOptions = Omit< | 'sortOrder' | 'typeToNamespacesMap' | 'migrationVersionCompatibility' - | 'downwardConversion' >; /** diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_get.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_get.ts index f45946add7132..3a135d4b73ce8 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_get.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_get.ts @@ -65,7 +65,7 @@ export const performBulkGet = async ( const { securityExtension, spacesExtension } = extensions; const namespace = commonHelper.getCurrentNamespace(options.namespace); - const { migrationVersionCompatibility, downwardConversion } = options; + const { migrationVersionCompatibility } = options; if (objects.length === 0) { return { saved_objects: [] }; @@ -204,7 +204,7 @@ export const performBulkGet = async ( const document = getSavedObjectFromSource(registry, type, id, doc, { migrationVersionCompatibility, }); - const migrated = migrationHelper.migrateStorageDocument(document, { downwardConversion }); + const migrated = migrationHelper.migrateStorageDocument(document); return migrated; }), diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_resolve.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_resolve.ts index f937f3646ec26..a18b69ec29bf6 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_resolve.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_resolve.ts @@ -10,7 +10,7 @@ import { type SavedObject, BulkResolveError } from '@kbn/core-saved-objects-serv import { SavedObjectsBulkResolveObject, SavedObjectsBulkResolveResponse, - SavedObjectsGetOptions, + SavedObjectsResolveOptions, SavedObjectsResolveResponse, } from '@kbn/core-saved-objects-api-server'; import { errorContent } from './utils'; @@ -20,7 +20,7 @@ import { incrementCounterInternal } from './internals/increment_counter_internal export interface PerformCreateParams { objects: SavedObjectsBulkResolveObject[]; - options: SavedObjectsGetOptions; + options: SavedObjectsResolveOptions; } export const performBulkResolve = async ( @@ -52,7 +52,7 @@ export const performBulkResolve = async ( encryptionExtension, securityExtension, objects, - options: { ...options, namespace }, // note: Includes downwardConversion?: 'forbid' + options: { ...options, namespace }, }); const resolvedObjects = bulkResults.map>((result) => { // extract payloads from saved object errors diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/find.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/find.ts index c46f4c3eda8ea..3346e6552646a 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/find.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/find.ts @@ -92,7 +92,6 @@ export const performFind = async ( preference, aggs, migrationVersionCompatibility, - downwardConversion, } = options; if (!type) { @@ -245,9 +244,7 @@ export const performFind = async ( }); // can't migrate a document with partial attributes if (!fields) { - savedObject = migrationHelper.migrateStorageDocument(savedObject, { - downwardConversion, - }) as SavedObject; + savedObject = migrationHelper.migrateStorageDocument(savedObject) as SavedObject; } return { ...savedObject, diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/get.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/get.ts index edc9fdcb8106e..215036f29583c 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/get.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/get.ts @@ -42,7 +42,7 @@ export const performGet = async ( const { securityExtension } = extensions; const namespace = commonHelper.getCurrentNamespace(options.namespace); - const { migrationVersionCompatibility, downwardConversion } = options; + const { migrationVersionCompatibility } = options; if (!allowedTypes.includes(type)) { throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); @@ -86,9 +86,7 @@ export const performGet = async ( let migrated: SavedObject; try { - migrated = migrationHelper.migrateStorageDocument(document, { - downwardConversion, - }) as SavedObject; + migrated = migrationHelper.migrateStorageDocument(document) as SavedObject; } catch (error) { throw SavedObjectsErrorHelpers.decorateGeneralError( error, diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/helpers/migration.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/helpers/migration.ts index 42af825280f4f..c7ae86b101984 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/helpers/migration.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/helpers/migration.ts @@ -35,17 +35,8 @@ export class MigrationHelper { * Migrate the given SO document, accepting downgrades. * This function is meant to be used by read APIs (get, find) for documents fetched from the index. * It will therefore accept downgrading the document before returning it from the API. - * - * Note: to opt out of downgrades, use the downwardConversion: 'forbid' API option in READ API operations: - * get, resolve, find, bulk_get, bulk_resolve */ - migrateStorageDocument( - document: SavedObjectUnsanitizedDoc, - options: { downwardConversion?: 'allow' | 'forbid' } - ): SavedObjectUnsanitizedDoc { - return this.migrator.migrateDocument(document, { - allowDowngrade: - options?.downwardConversion && options.downwardConversion === 'forbid' ? false : true, - }); // allowDowngrade conditional on downwardConversion + migrateStorageDocument(document: SavedObjectUnsanitizedDoc): SavedObjectUnsanitizedDoc { + return this.migrator.migrateDocument(document, { allowDowngrade: true }); } } diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/internals/internal_bulk_resolve.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/internals/internal_bulk_resolve.ts index 57748b95ef0d5..53cb04453555c 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/internals/internal_bulk_resolve.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/internals/internal_bulk_resolve.ts @@ -11,7 +11,7 @@ import type { MgetResponseItem } from '@elastic/elasticsearch/lib/api/typesWithB import { isNotFoundFromUnsupportedServer } from '@kbn/core-elasticsearch-server-internal'; import type { SavedObjectsBulkResolveObject, - SavedObjectsGetOptions, + SavedObjectsResolveOptions, SavedObjectsResolveResponse, SavedObjectsIncrementCounterField, SavedObjectsIncrementCounterOptions, @@ -74,7 +74,7 @@ export interface InternalBulkResolveParams { encryptionExtension: ISavedObjectsEncryptionExtension | undefined; securityExtension: ISavedObjectsSecurityExtension | undefined; objects: SavedObjectsBulkResolveObject[]; - options?: SavedObjectsGetOptions; + options?: SavedObjectsResolveOptions; } /** @@ -120,7 +120,7 @@ export async function internalBulkResolve( const validObjects = allObjects.filter(isRight); const namespace = normalizeNamespace(options.namespace); - const { migrationVersionCompatibility, downwardConversion } = options; + const { migrationVersionCompatibility } = options; const aliasDocs = await fetchAndUpdateAliases( validObjects, @@ -186,11 +186,8 @@ export async function internalBulkResolve( // @ts-expect-error MultiGetHit._source is optional const object = getSavedObjectFromSource(registry, objectType, objectId, doc, { migrationVersionCompatibility, - downwardConversion, }); - const migrated = migrator.migrateDocument(object, { - allowDowngrade: downwardConversion && downwardConversion === 'forbid' ? false : true, // 'forbid' => docMigrator throws on when documents have higher model versions than current. - }) as SavedObject; + const migrated = migrator.migrateDocument(object, { allowDowngrade: true }) as SavedObject; if (!encryptionExtension?.isEncryptableType(migrated.type)) { return migrated; diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/resolve.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/resolve.ts index 48d78181ba571..50de472546344 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/resolve.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/resolve.ts @@ -7,7 +7,7 @@ */ import { - SavedObjectsGetOptions, + SavedObjectsResolveOptions, SavedObjectsResolveResponse, } from '@kbn/core-saved-objects-api-server'; import { ApiExecutionContext } from './types'; @@ -17,7 +17,7 @@ import { incrementCounterInternal } from './internals/increment_counter_internal export interface PerformCreateParams { type: string; id: string; - options: SavedObjectsGetOptions; + options: SavedObjectsResolveOptions; } export const performResolve = async ( @@ -51,7 +51,7 @@ export const performResolve = async ( encryptionExtension, securityExtension, objects: [{ type, id }], - options: { ...options, namespace }, // note: Includes downwardConversion?: 'forbid' + options: { ...options, namespace }, }); const [result] = bulkResults; if (isBulkResolveError(result)) { diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.ts index beb47219985f4..321c5811ea6d8 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.ts @@ -35,6 +35,7 @@ import type { SavedObjectsClosePointInTimeResponse, ISavedObjectsPointInTimeFinder, SavedObjectsCreatePointInTimeFinderDependencies, + SavedObjectsResolveOptions, SavedObjectsResolveResponse, SavedObjectsCollectMultiNamespaceReferencesObject, SavedObjectsUpdateObjectsSpacesObject, @@ -347,7 +348,7 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { */ async bulkResolve( objects: SavedObjectsBulkResolveObject[], - options: SavedObjectsGetOptions = {} + options: SavedObjectsResolveOptions = {} ): Promise> { return await performBulkResolve( { @@ -382,7 +383,7 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { async resolve( type: string, id: string, - options: SavedObjectsGetOptions = {} + options: SavedObjectsResolveOptions = {} ): Promise> { return await performResolve( { diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/saved_objects_client.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/saved_objects_client.ts index 23f014d393ce0..925caae1061b7 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/saved_objects_client.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/saved_objects_client.ts @@ -31,6 +31,7 @@ import type { SavedObjectsBulkUpdateObject, ISavedObjectsPointInTimeFinder, SavedObjectsCreatePointInTimeFinderDependencies, + SavedObjectsResolveOptions, SavedObjectsResolveResponse, SavedObjectsCollectMultiNamespaceReferencesObject, SavedObjectsUpdateObjectsSpacesObject, @@ -121,7 +122,7 @@ export class SavedObjectsClient implements SavedObjectsClientContract { /** {@inheritDoc SavedObjectsClientContract.bulkResolve} */ async bulkResolve( objects: SavedObjectsBulkResolveObject[], - options?: SavedObjectsGetOptions + options?: SavedObjectsResolveOptions ): Promise> { return await this._repository.bulkResolve(objects, options); } @@ -130,7 +131,7 @@ export class SavedObjectsClient implements SavedObjectsClientContract { async resolve( type: string, id: string, - options: SavedObjectsGetOptions = {} + options: SavedObjectsResolveOptions = {} ): Promise> { return await this._repository.resolve(type, id, options); } diff --git a/packages/core/saved-objects/core-saved-objects-api-server/index.ts b/packages/core/saved-objects/core-saved-objects-api-server/index.ts index 809a8e4f98945..a3cb5fa4045a4 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server/index.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server/index.ts @@ -44,6 +44,7 @@ export type { ISavedObjectsPointInTimeFinder, SavedObjectsCreatePointInTimeFinderDependencies, SavedObjectsPitParams, + SavedObjectsResolveOptions, SavedObjectsResolveResponse, SavedObjectsCollectMultiNamespaceReferencesObject, SavedObjectsUpdateObjectsSpacesResponseObject, diff --git a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts index a6b3c085dcb6c..e0e035abea34b 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts @@ -11,7 +11,7 @@ import type { AggregationsAggregationContainer, SortResults, } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import type { SavedObject, SavedObjectsGetOptions } from '../..'; +import type { SavedObject } from '../..'; type KueryNode = any; @@ -153,8 +153,7 @@ export interface SavedObjectsFindOptions { */ pit?: SavedObjectsPitParams; /** {@link SavedObjectsRawDocParseOptions.migrationVersionCompatibility} */ - migrationVersionCompatibility?: SavedObjectsGetOptions['migrationVersionCompatibility']; - downwardConversion?: SavedObjectsGetOptions['downwardConversion']; + migrationVersionCompatibility?: 'compatible' | 'raw'; } /** diff --git a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/get.ts b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/get.ts index 48a6ecb9b9dd4..884e7cba4f570 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/get.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/get.ts @@ -16,5 +16,4 @@ import { SavedObjectsBaseOptions } from './base'; export interface SavedObjectsGetOptions extends SavedObjectsBaseOptions { /** {@link SavedObjectsRawDocParseOptions.migrationVersionCompatibility} */ migrationVersionCompatibility?: 'compatible' | 'raw'; - downwardConversion?: 'allow' | 'forbid'; } diff --git a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/index.ts b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/index.ts index b01010c100a77..ab3c3ca12f572 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/index.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/index.ts @@ -65,7 +65,7 @@ export type { SavedObjectsRemoveReferencesToOptions, SavedObjectsRemoveReferencesToResponse, } from './remove_references_to'; -export type { SavedObjectsResolveResponse } from './resolve'; +export type { SavedObjectsResolveOptions, SavedObjectsResolveResponse } from './resolve'; export type { SavedObjectsUpdateResponse, SavedObjectsUpdateOptions } from './update'; export type { SavedObjectsUpdateObjectsSpacesObject, diff --git a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/resolve.ts b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/resolve.ts index b15075a67aa0c..d32f36bdcce4f 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/resolve.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/resolve.ts @@ -6,8 +6,19 @@ * Side Public License, v 1. */ +import { SavedObjectsBaseOptions } from './base'; import type { SavedObject } from '../..'; +/** + * Options for the saved objects get operation + * + * @public + */ +export interface SavedObjectsResolveOptions extends SavedObjectsBaseOptions { + /** {@link SavedObjectsRawDocParseOptions.migrationVersionCompatibility} */ + migrationVersionCompatibility?: 'compatible' | 'raw'; +} + /** * * @public diff --git a/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_client.ts b/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_client.ts index b81f5c3b6349f..532c2dc58d992 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_client.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_client.ts @@ -20,6 +20,7 @@ import type { SavedObjectsUpdateObjectsSpacesOptions, SavedObjectsCollectMultiNamespaceReferencesObject, SavedObjectsUpdateObjectsSpacesResponse, + SavedObjectsResolveOptions, SavedObjectsResolveResponse, ISavedObjectsPointInTimeFinder, SavedObjectsRemoveReferencesToOptions, @@ -216,7 +217,7 @@ export interface SavedObjectsClientContract { * See documentation for `.resolve`. * * @param objects - an array of objects to resolve (contains id and type) - * @param options {@link SavedObjectsGetOptions} - options for the bulk resolve operation + * @param options {@link SavedObjectsResolveOptions} - options for the bulk resolve operation * @returns the {@link SavedObjectsBulkResolveResponse} * @example * @@ -231,7 +232,7 @@ export interface SavedObjectsClientContract { */ bulkResolve( objects: SavedObjectsBulkResolveObject[], - options?: SavedObjectsGetOptions + options?: SavedObjectsResolveOptions ): Promise>; /** @@ -247,13 +248,13 @@ export interface SavedObjectsClientContract { * * @param type - The type of SavedObject to retrieve * @param id - The ID of the SavedObject to retrieve - * @param options {@link SavedObjectsGetOptions} - options for the resolve operation + * @param options {@link SavedObjectsResolveOptions} - options for the resolve operation * @returns the {@link SavedObjectsResolveResponse} */ resolve( type: string, id: string, - options?: SavedObjectsGetOptions + options?: SavedObjectsResolveOptions ): Promise>; /** diff --git a/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts b/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts index 85dff61775391..dc748e48123ad 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts @@ -20,6 +20,7 @@ import type { SavedObjectsUpdateObjectsSpacesOptions, SavedObjectsCollectMultiNamespaceReferencesObject, SavedObjectsUpdateObjectsSpacesResponse, + SavedObjectsResolveOptions, SavedObjectsResolveResponse, ISavedObjectsPointInTimeFinder, SavedObjectsRemoveReferencesToOptions, @@ -200,7 +201,7 @@ export interface ISavedObjectsRepository { * Resolves an array of objects by id, using any legacy URL aliases if they exist * * @param {array} objects - an array of objects containing id, type - * @param {object} [options={}] {@link SavedObjectsGetOptions} - options for the bulk resolve operation + * @param {object} [options={}] {@link SavedObjectsResolveOptions} - options for the bulk resolve operation * @property {string} [options.migrationVersionCompatibility] * @property {string} [options.namespace] * @returns {promise} - { resolved_objects: [{ saved_object, outcome }] } @@ -213,7 +214,7 @@ export interface ISavedObjectsRepository { */ bulkResolve( objects: SavedObjectsBulkResolveObject[], - options?: SavedObjectsGetOptions + options?: SavedObjectsResolveOptions ): Promise>; /** @@ -237,7 +238,7 @@ export interface ISavedObjectsRepository { * * @param {string} type - the type of the object to resolve * @param {string} id - the id of the object to resolve - * @param {object} [options={}] {@link SavedObjectsGetOptions} - options for the resolve operation + * @param {object} [options={}] {@link SavedObjectsResolveOptions} - options for the resolve operation * @property {string} [options.migrationVersionCompatibility] * @property {string} [options.namespace] * @returns {promise} - { saved_object, outcome } @@ -245,7 +246,7 @@ export interface ISavedObjectsRepository { resolve( type: string, id: string, - options?: SavedObjectsGetOptions + options?: SavedObjectsResolveOptions ): Promise>; /** diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.ts index 5e132143d5c6b..2e3a99b4b463e 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.ts @@ -1462,7 +1462,7 @@ describe('DocumentMigrator', () => { expect(() => migrator.migrate(document, { allowDowngrade: false }) ).toThrowErrorMatchingInlineSnapshot( - `"[NewerModelVersionError]: Document \\"smelly\\" belongs to a more recent version of Kibana [10.2.0] when the last known version is [10.1.0]."` + `"Document \\"smelly\\" belongs to a more recent version of Kibana [10.2.0] when the last known version is [10.1.0]."` ); }); }); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.ts index 387e037e7a0bf..8ca15093d5827 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.ts @@ -81,9 +81,6 @@ export interface VersionedTransformer { migrateAndConvert(doc: SavedObjectUnsanitizedDoc): SavedObjectUnsanitizedDoc[]; } -export function createNewerModelVersionError(message: string) { - return Boom.boomify(Boom.badData(message), { message: '[NewerModelVersionError]' }); -} /** * A concrete implementation of the {@link VersionedTransformer} interface. */ @@ -184,11 +181,9 @@ export class DocumentMigrator implements VersionedTransformer { const currentVersion = doc.typeMigrationVersion ?? doc.migrationVersion?.[doc.type]; const latestVersion = this.migrations[doc.type].latestVersion[TransformType.Migrate]; if (!allowDowngrade) { - if (!allowDowngrade) { - throw createNewerModelVersionError( - `Document "${doc.id}" belongs to a more recent version of Kibana [${currentVersion}] when the last known version is [${latestVersion}].` - ); - } + throw Boom.badData( + `Document "${doc.id}" belongs to a more recent version of Kibana [${currentVersion}] when the last known version is [${latestVersion}].` + ); } return this.transformDown(doc, { targetTypeVersion: latestVersion! }); } else { diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 536b3f883cac6..dc07f316c5244 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -96,6 +96,7 @@ pageLoadAssetSize: monitoring: 80000 navigation: 37269 newsfeed: 42228 + noDataPage: 5000 observability: 115443 observabilityAIAssistant: 25000 observabilityOnboarding: 19573 diff --git a/packages/kbn-search-api-panels/components/ingest_data.tsx b/packages/kbn-search-api-panels/components/ingest_data.tsx index 5e1c8e7bc01dd..11f42fb6e7833 100644 --- a/packages/kbn-search-api-panels/components/ingest_data.tsx +++ b/packages/kbn-search-api-panels/components/ingest_data.tsx @@ -21,7 +21,12 @@ interface IngestDataProps { codeSnippet: string; selectedLanguage: LanguageDefinition; setSelectedLanguage: (language: LanguageDefinition) => void; - docLinks: any; + docLinks: { + beats: string; + connectors: string; + integrations: string; + logStash: string; + }; assetBasePath: string; application?: ApplicationStart; sharePlugin: SharePluginStart; diff --git a/packages/kbn-search-api-panels/components/integrations_panel.tsx b/packages/kbn-search-api-panels/components/integrations_panel.tsx index 4f5313dd93579..6d8e2e70526e1 100644 --- a/packages/kbn-search-api-panels/components/integrations_panel.tsx +++ b/packages/kbn-search-api-panels/components/integrations_panel.tsx @@ -24,7 +24,7 @@ import { LEARN_MORE_LABEL } from '../constants'; import { GithubLink } from './github_link'; export interface IntegrationsPanelProps { - docLinks: any; + docLinks: { beats: string; connectors: string; logStash: string }; assetBasePath: string; } diff --git a/packages/kbn-securitysolution-utils/src/transform_data_to_ndjson/index.test.ts b/packages/kbn-securitysolution-utils/src/transform_data_to_ndjson/index.test.ts index 32300a2e66c96..14f6201cc8283 100644 --- a/packages/kbn-securitysolution-utils/src/transform_data_to_ndjson/index.test.ts +++ b/packages/kbn-securitysolution-utils/src/transform_data_to_ndjson/index.test.ts @@ -20,7 +20,6 @@ const getRulesSchemaMock = (anchorDate: string = ANCHOR_DATE) => ({ enabled: true, false_positives: ['false positive 1', 'false positive 2'], from: 'now-6m', - investigation_fields: ['custom.field1', 'custom.field2'], immutable: false, name: 'Query with a rule id', query: 'user.name: root or user.name: admin', diff --git a/packages/kbn-test/jest-preset.js b/packages/kbn-test/jest-preset.js index f80a5e103c16f..bbe3cb2923280 100644 --- a/packages/kbn-test/jest-preset.js +++ b/packages/kbn-test/jest-preset.js @@ -105,8 +105,10 @@ module.exports = { transformIgnorePatterns: [ // ignore all node_modules except monaco-editor and react-monaco-editor which requires babel transforms to handle dynamic import() // since ESM modules are not natively supported in Jest yet (https://github.com/facebook/jest/issues/4842) - '[/\\\\]node_modules(?![\\/\\\\](byte-size|monaco-editor|monaco-yaml|vscode-languageserver-types|react-monaco-editor|d3-interpolate|d3-color))[/\\\\].+\\.js$', + '[/\\\\]node_modules(?![\\/\\\\](byte-size|monaco-editor|monaco-yaml|vscode-languageserver-types|react-monaco-editor|d3-interpolate|d3-color|langchain|langsmith))[/\\\\].+\\.js$', 'packages/kbn-pm/dist/index.js', + '[/\\\\]node_modules(?![\\/\\\\](langchain|langsmith))/dist/[/\\\\].+\\.js$', + '[/\\\\]node_modules(?![\\/\\\\](langchain|langsmith))/dist/util/[/\\\\].+\\.js$', ], // An array of regexp pattern strings that are matched against all source file paths, matched files to include/exclude for code coverage diff --git a/packages/kbn-test/jest_integration_node/jest-preset.js b/packages/kbn-test/jest_integration_node/jest-preset.js index 43373e41db5c1..92b8aedb5ee88 100644 --- a/packages/kbn-test/jest_integration_node/jest-preset.js +++ b/packages/kbn-test/jest_integration_node/jest-preset.js @@ -19,6 +19,13 @@ module.exports = { testPathIgnorePatterns: preset.testPathIgnorePatterns.filter( (pattern) => !pattern.includes('integration_tests') ), + // An array of regexp pattern strings that are matched against, matched files will skip transformation: + transformIgnorePatterns: [ + // since ESM modules are not natively supported in Jest yet (https://github.com/facebook/jest/issues/4842) + '[/\\\\]node_modules(?![\\/\\\\](langchain|langsmith))[/\\\\].+\\.js$', + '[/\\\\]node_modules(?![\\/\\\\](langchain|langsmith))/dist/[/\\\\].+\\.js$', + '[/\\\\]node_modules(?![\\/\\\\](langchain|langsmith))/dist/util/[/\\\\].+\\.js$', + ], setupFilesAfterEnv: [ '/packages/kbn-test/src/jest/setup/after_env.integration.js', '/packages/kbn-test/src/jest/setup/mocks.moment_timezone.js', diff --git a/packages/kbn-test/src/jest/setup/setup_test.js b/packages/kbn-test/src/jest/setup/setup_test.js index b0038daf196c9..ee386f894a71e 100644 --- a/packages/kbn-test/src/jest/setup/setup_test.js +++ b/packages/kbn-test/src/jest/setup/setup_test.js @@ -13,6 +13,7 @@ import 'jest-styled-components'; import '@testing-library/jest-dom'; +import 'web-streams-polyfill/es6'; // ReadableStream polyfill /** * Removed in Jest 27/jsdom, used in some transitive dependencies diff --git a/packages/kbn-unified-field-list/src/components/field_list/field_list.tsx b/packages/kbn-unified-field-list/src/components/field_list/field_list.tsx index 9f51fd99e0ed4..8261d5795b61a 100644 --- a/packages/kbn-unified-field-list/src/components/field_list/field_list.tsx +++ b/packages/kbn-unified-field-list/src/components/field_list/field_list.tsx @@ -56,7 +56,14 @@ export const FieldList: React.FC = ({ css={containerStyle} className={className} > - {isProcessing && } + {isProcessing && ( + + )} {!!prepend && {prepend}} {children} {!!append && {append}} diff --git a/packages/shared-ux/card/no_data/impl/src/no_data_card.tsx b/packages/shared-ux/card/no_data/impl/src/no_data_card.tsx index 22126c6b335e0..2fd29d42224ee 100644 --- a/packages/shared-ux/card/no_data/impl/src/no_data_card.tsx +++ b/packages/shared-ux/card/no_data/impl/src/no_data_card.tsx @@ -35,7 +35,9 @@ export const NoDataCard = ({ href: srcHref, category, description, ...props }: P return ( - + ); }; diff --git a/packages/shared-ux/card/no_data/types/index.d.ts b/packages/shared-ux/card/no_data/types/index.d.ts index 5b2a0b090ffe2..e52843b160639 100644 --- a/packages/shared-ux/card/no_data/types/index.d.ts +++ b/packages/shared-ux/card/no_data/types/index.d.ts @@ -79,4 +79,4 @@ export type NoDataCardComponentProps = Partial< /** * Props for the `NoDataCard` sevice-connected component. */ -export type NoDataCardProps = Omit; +export type NoDataCardProps = NoDataCardComponentProps; diff --git a/packages/shared-ux/page/analytics_no_data/impl/src/analytics_no_data_page.component.test.tsx b/packages/shared-ux/page/analytics_no_data/impl/src/analytics_no_data_page.component.test.tsx index 1f657f642fc47..4e16dd6c38bc0 100644 --- a/packages/shared-ux/page/analytics_no_data/impl/src/analytics_no_data_page.component.test.tsx +++ b/packages/shared-ux/page/analytics_no_data/impl/src/analytics_no_data_page.component.test.tsx @@ -10,7 +10,10 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; import { mountWithIntl } from '@kbn/test-jest-helpers'; +import { I18nProvider } from '@kbn/i18n-react'; + import { KibanaNoDataPage } from '@kbn/shared-ux-page-kibana-no-data'; +import { render, screen } from '@testing-library/react'; import { AnalyticsNoDataPage } from './analytics_no_data_page.component'; import { AnalyticsNoDataPageProvider } from './services'; @@ -28,6 +31,7 @@ describe('AnalyticsNoDataPageComponent', () => { onDataViewCreated={onDataViewCreated} kibanaGuideDocLink={'http://www.test.com'} showPlainSpinner={false} + prependBasePath={(path: string) => path} /> ); @@ -52,6 +56,7 @@ describe('AnalyticsNoDataPageComponent', () => { kibanaGuideDocLink={'http://www.test.com'} allowAdHocDataView={true} showPlainSpinner={false} + prependBasePath={(path: string) => path} /> ); @@ -61,4 +66,86 @@ describe('AnalyticsNoDataPageComponent', () => { expect(component.find(KibanaNoDataPage).length).toBe(1); expect(component.find(KibanaNoDataPage).props().allowAdHocDataView).toBe(true); }); + + describe('no data state', () => { + describe('kibana flavor', () => { + it('renders add integrations card', async () => { + render( + + false }}> + path} + /> + + + ); + + await screen.findByTestId('kbnOverviewAddIntegrations'); + await screen.getAllByText('Add integrations'); + }); + + it('renders disabled add integrations card when fleet is not available', async () => { + render( + + false, canAccessFleet: false }} + > + path} + /> + + + ); + + await screen.findByTestId('kbnOverviewAddIntegrations'); + await screen.getByText('Contact your administrator'); + }); + }); + + describe('serverless_search flavor', () => { + it('renders getting started card', async () => { + render( + + false }}> + path} + /> + + + ); + + await screen.findByTestId('kbnOverviewElasticsearchGettingStarted'); + }); + + it('renders the same getting started card when fleet is not available', async () => { + render( + + false, canAccessFleet: false }} + > + path} + pageFlavor={'serverless_search'} + /> + + + ); + + await screen.findByTestId('kbnOverviewElasticsearchGettingStarted'); + }); + }); + }); }); diff --git a/packages/shared-ux/page/analytics_no_data/impl/src/analytics_no_data_page.component.tsx b/packages/shared-ux/page/analytics_no_data/impl/src/analytics_no_data_page.component.tsx index d67cb082f5539..4c22a0acb2475 100644 --- a/packages/shared-ux/page/analytics_no_data/impl/src/analytics_no_data_page.component.tsx +++ b/packages/shared-ux/page/analytics_no_data/impl/src/analytics_no_data_page.component.tsx @@ -8,6 +8,8 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { KibanaNoDataPage } from '@kbn/shared-ux-page-kibana-no-data'; +import { KibanaNoDataPageProps } from '@kbn/shared-ux-page-kibana-no-data-types'; +import { AnalyticsNoDataPageFlavor } from '@kbn/shared-ux-page-analytics-no-data-types'; /** * Props for the pure component. @@ -21,26 +23,63 @@ export interface Props { allowAdHocDataView?: boolean; /** if the kibana instance is customly branded */ showPlainSpinner: boolean; + /** The flavor of the empty page to use. */ + pageFlavor?: AnalyticsNoDataPageFlavor; + prependBasePath: (path: string) => string; } -const solution = i18n.translate('sharedUXPackages.noDataConfig.analytics', { - defaultMessage: 'Analytics', -}); - -const pageTitle = i18n.translate('sharedUXPackages.noDataConfig.analyticsPageTitle', { - defaultMessage: 'Welcome to Analytics!', -}); - -const addIntegrationsTitle = i18n.translate('sharedUXPackages.noDataConfig.addIntegrationsTitle', { - defaultMessage: 'Add integrations', -}); - -const addIntegrationsDescription = i18n.translate( - 'sharedUXPackages.noDataConfig.addIntegrationsDescription', - { - defaultMessage: 'Use Elastic Agent to collect data and build out Analytics solutions.', - } -); +const flavors: { + [K in AnalyticsNoDataPageFlavor]: (deps: { + kibanaGuideDocLink: string; + prependBasePath: (path: string) => string; + }) => KibanaNoDataPageProps['noDataConfig']; +} = { + kibana: ({ kibanaGuideDocLink }) => ({ + solution: i18n.translate('sharedUXPackages.noDataConfig.analytics', { + defaultMessage: 'Analytics', + }), + pageTitle: i18n.translate('sharedUXPackages.noDataConfig.analyticsPageTitle', { + defaultMessage: 'Welcome to Analytics!', + }), + logo: 'logoKibana', + action: { + elasticAgent: { + title: i18n.translate('sharedUXPackages.noDataConfig.addIntegrationsTitle', { + defaultMessage: 'Add integrations', + }), + description: i18n.translate('sharedUXPackages.noDataConfig.addIntegrationsDescription', { + defaultMessage: 'Use Elastic Agent to collect data and build out Analytics solutions.', + }), + 'data-test-subj': 'kbnOverviewAddIntegrations', + }, + }, + docsLink: kibanaGuideDocLink, + }), + serverless_search: ({ prependBasePath }) => ({ + solution: i18n.translate('sharedUXPackages.noDataConfig.elasticsearch', { + defaultMessage: 'Elasticsearch', + }), + pageTitle: i18n.translate('sharedUXPackages.noDataConfig.elasticsearchPageTitle', { + defaultMessage: 'Welcome to Elasticsearch!', + }), + logo: 'logoElasticsearch', + action: { + elasticsearch: { + title: i18n.translate('sharedUXPackages.noDataConfig.elasticsearchTitle', { + defaultMessage: 'Get started', + }), + description: i18n.translate('sharedUXPackages.noDataConfig.elasticsearchDescription', { + defaultMessage: + 'Set up your programming language client, ingest some data, and start searching.', + }), + 'data-test-subj': 'kbnOverviewElasticsearchGettingStarted', + href: prependBasePath('/app/elasticsearch/'), + /** force the no data card to be shown **/ + canAccessFleet: true, + }, + }, + }), +}; /** * A pure component of an entire page that can be displayed when Kibana "has no data", specifically for Analytics. @@ -50,20 +89,13 @@ export const AnalyticsNoDataPage = ({ onDataViewCreated, allowAdHocDataView, showPlainSpinner, + prependBasePath, + pageFlavor = 'kibana', }: Props) => { - const noDataConfig = { - solution, - pageTitle, - logo: 'logoKibana', - action: { - elasticAgent: { - title: addIntegrationsTitle, - description: addIntegrationsDescription, - 'data-test-subj': 'kbnOverviewAddIntegrations', - }, - }, - docsLink: kibanaGuideDocLink, - }; + const noDataConfig: KibanaNoDataPageProps['noDataConfig'] = flavors[pageFlavor]({ + kibanaGuideDocLink, + prependBasePath, + }); return ( { expect(component.find(Component).props().kibanaGuideDocLink).toBe(services.kibanaGuideDocLink); expect(component.find(Component).props().onDataViewCreated).toBe(onDataViewCreated); expect(component.find(Component).props().allowAdHocDataView).toBe(true); + expect(component.find(Component).props().prependBasePath).toBe(services.prependBasePath); + expect(component.find(Component).props().pageFlavor).toBe(services.pageFlavor); }); it('passes correct boolean value to showPlainSpinner', () => { diff --git a/packages/shared-ux/page/analytics_no_data/impl/src/analytics_no_data_page.tsx b/packages/shared-ux/page/analytics_no_data/impl/src/analytics_no_data_page.tsx index 9b600c374dd02..e9d3ee318d3fd 100644 --- a/packages/shared-ux/page/analytics_no_data/impl/src/analytics_no_data_page.tsx +++ b/packages/shared-ux/page/analytics_no_data/impl/src/analytics_no_data_page.tsx @@ -21,7 +21,7 @@ export const AnalyticsNoDataPage = ({ allowAdHocDataView, }: AnalyticsNoDataPageProps) => { const services = useServices(); - const { kibanaGuideDocLink, customBranding } = services; + const { kibanaGuideDocLink, customBranding, prependBasePath, pageFlavor } = services; const { hasCustomBranding$ } = customBranding; const showPlainSpinner = useObservable(hasCustomBranding$) ?? false; @@ -32,6 +32,8 @@ export const AnalyticsNoDataPage = ({ allowAdHocDataView, kibanaGuideDocLink, showPlainSpinner, + prependBasePath, + pageFlavor, }} /> ); diff --git a/packages/shared-ux/page/analytics_no_data/impl/src/services.tsx b/packages/shared-ux/page/analytics_no_data/impl/src/services.tsx index 991893aeca501..4d514ba032ec9 100644 --- a/packages/shared-ux/page/analytics_no_data/impl/src/services.tsx +++ b/packages/shared-ux/page/analytics_no_data/impl/src/services.tsx @@ -27,10 +27,10 @@ export const AnalyticsNoDataPageProvider: FC = ({ children, ...services }) => { - const { kibanaGuideDocLink, customBranding } = services; + const { kibanaGuideDocLink, customBranding, prependBasePath, pageFlavor } = services; return ( - + {children} ); @@ -48,6 +48,8 @@ export const AnalyticsNoDataPageKibanaProvider: FC diff --git a/packages/shared-ux/page/analytics_no_data/impl/tsconfig.json b/packages/shared-ux/page/analytics_no_data/impl/tsconfig.json index 6a78f24dff0f7..4b9192a9fd714 100644 --- a/packages/shared-ux/page/analytics_no_data/impl/tsconfig.json +++ b/packages/shared-ux/page/analytics_no_data/impl/tsconfig.json @@ -19,6 +19,8 @@ "@kbn/shared-ux-page-analytics-no-data-types", "@kbn/test-jest-helpers", "@kbn/shared-ux-page-analytics-no-data-mocks", + "@kbn/shared-ux-page-kibana-no-data-types", + "@kbn/i18n-react", ], "exclude": [ "target/**/*", diff --git a/packages/shared-ux/page/analytics_no_data/mocks/src/jest.ts b/packages/shared-ux/page/analytics_no_data/mocks/src/jest.ts index 98885d55ba47d..f45d0f72ffed9 100644 --- a/packages/shared-ux/page/analytics_no_data/mocks/src/jest.ts +++ b/packages/shared-ux/page/analytics_no_data/mocks/src/jest.ts @@ -15,6 +15,8 @@ export const getServicesMock = () => { ...getKibanaNoDataPageServicesMock(), kibanaGuideDocLink: 'Kibana guide', customBranding: { hasCustomBranding$: of(false) }, + prependBasePath: (path) => path, + pageFlavor: 'kibana', }; return services; @@ -26,6 +28,8 @@ export const getServicesMockCustomBranding = () => { // this mock will have custom branding set to true customBranding: { hasCustomBranding$: of(true) }, kibanaGuideDocLink: 'Kibana guide', + prependBasePath: (path) => path, + pageFlavor: 'kibana', }; return services; diff --git a/packages/shared-ux/page/analytics_no_data/mocks/src/storybook.ts b/packages/shared-ux/page/analytics_no_data/mocks/src/storybook.ts index 86bf25dbde9e9..6bb3f07e34a87 100644 --- a/packages/shared-ux/page/analytics_no_data/mocks/src/storybook.ts +++ b/packages/shared-ux/page/analytics_no_data/mocks/src/storybook.ts @@ -51,6 +51,8 @@ export class StorybookMock extends AbstractStorybookMock< customBranding: { hasCustomBranding$: of(false), }, + pageFlavor: 'kibana', + prependBasePath: (path) => path, ...kibanaNoDataMock.getServices(params), }; } diff --git a/packages/shared-ux/page/analytics_no_data/types/index.d.ts b/packages/shared-ux/page/analytics_no_data/types/index.d.ts index 4e54315f071dd..f292e297b6fdc 100644 --- a/packages/shared-ux/page/analytics_no_data/types/index.d.ts +++ b/packages/shared-ux/page/analytics_no_data/types/index.d.ts @@ -17,6 +17,8 @@ import { Observable } from 'rxjs'; export interface Services { kibanaGuideDocLink: string; customBranding: { hasCustomBranding$: Observable }; + prependBasePath: (path: string) => string; + pageFlavor: AnalyticsNoDataPageFlavor; } /** @@ -24,6 +26,8 @@ export interface Services { */ export type AnalyticsNoDataPageServices = Services & KibanaNoDataPageServices; +export type AnalyticsNoDataPageFlavor = 'kibana' | 'serverless_search'; + export interface KibanaDependencies { coreStart: { docLinks: { @@ -36,6 +40,14 @@ export interface KibanaDependencies { customBranding: { hasCustomBranding$: Observable; }; + http: { + basePath: { + prepend: (path: string) => string; + }; + }; + }; + noDataPage?: { + getAnalyticsNoDataPageFlavor: () => AnalyticsNoDataPageFlavor; }; } diff --git a/packages/shared-ux/page/no_data/impl/src/no_data_page.tsx b/packages/shared-ux/page/no_data/impl/src/no_data_page.tsx index 1ba9b18049e87..5aec81d942de6 100644 --- a/packages/shared-ux/page/no_data/impl/src/no_data_page.tsx +++ b/packages/shared-ux/page/no_data/impl/src/no_data_page.tsx @@ -33,13 +33,13 @@ export const NoDataPage = ({ values: { solution }, }); - const link = ( + const link = docsLink ? ( - ); + ) : null; - const message = ( + const message = link ? ( + ) : ( + ); return ( diff --git a/packages/shared-ux/page/no_data/types/index.d.ts b/packages/shared-ux/page/no_data/types/index.d.ts index 3db9e80c950c9..6f29e5ab08d7a 100644 --- a/packages/shared-ux/page/no_data/types/index.d.ts +++ b/packages/shared-ux/page/no_data/types/index.d.ts @@ -31,9 +31,9 @@ export interface NoDataPageProps extends CommonProps, ActionCardProps { */ solution: string; /** - * Required to set the docs link for the whole solution + * Required in "kibana" flavor to set the docs link for the whole solution, otherwise optional */ - docsLink: string; + docsLink?: string; /** * Optionally replace the auto-generated logo */ diff --git a/src/core/server/integration_tests/saved_objects/migrations/zdt_2/sor_higher_version_docs.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_2/sor_higher_version_docs.test.ts index 960752c63d369..e431c3607d983 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/zdt_2/sor_higher_version_docs.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_2/sor_higher_version_docs.test.ts @@ -138,26 +138,6 @@ describe('Higher version doc conversion', () => { newField: 'someValue', }); }); - it('throws error for documents using higher version model than current', async () => { - try { - await repositoryV1.get('test-type', 'doc-1', { - downwardConversion: 'forbid', - }); - } catch (err) { - expect(err.message).toBe( - '[NewerModelVersionError]: Document "doc-1" belongs to a more recent version of Kibana [10.2.0] when the last known version is [10.1.0].' - ); - } - }); - it("doesn't throw error for documents using current version model when 'downwardConversion' is 'forbid'", async () => { - try { - await repositoryV2.get('test-type', 'doc-1', { - downwardConversion: 'forbid', - }); - } catch (err) { - expect(err).toBeUndefined(); - } - }); }); describe('#bulkGet', () => { @@ -175,26 +155,6 @@ describe('Higher version doc conversion', () => { newField: 'someValue', }); }); - it('throws error for documents using higher version model than current', async () => { - try { - await repositoryV2.bulkGet([{ type: 'test-type', id: 'doc-1' }], { - downwardConversion: 'forbid', - }); - } catch (err) { - expect(err.message).toBe( - '[NewerModelVersionError]: Document "doc-1" belongs to a more recent version of Kibana [10.2.0] when the last known version is [10.1.0].' - ); - } - }); - it("doesn't throw error for documents using current version model when 'downwardConversion' is 'forbid'", async () => { - try { - await repositoryV2.get('test-type', 'doc-1', { - downwardConversion: 'forbid', - }); - } catch (err) { - expect(err).toBeUndefined(); - } - }); }); describe('#resolve', () => { @@ -212,26 +172,6 @@ describe('Higher version doc conversion', () => { newField: 'someValue', }); }); - it('throws error for documents using higher version model than current', async () => { - try { - await repositoryV2.resolve('test-type', 'doc-1', { - downwardConversion: 'forbid', - }); - } catch (err) { - expect(err.message).toBe( - '[NewerModelVersionError]: Document "doc-1" belongs to a more recent version of Kibana [10.2.0] when the last known version is [10.1.0].' - ); - } - }); - it("doesn't throw error for documents using current version model when 'downwardConversion' is 'forbid'", async () => { - try { - await repositoryV2.get('test-type', 'doc-1', { - downwardConversion: 'forbid', - }); - } catch (err) { - expect(err).toBeUndefined(); - } - }); }); describe('#bulkResolve', () => { @@ -249,25 +189,5 @@ describe('Higher version doc conversion', () => { newField: 'someValue', }); }); - it('throws error for documents using higher version model than current', async () => { - try { - await repositoryV2.bulkResolve([{ type: 'test-type', id: 'doc-1' }], { - downwardConversion: 'forbid', - }); - } catch (err) { - expect(err.message).toBe( - '[NewerModelVersionError]: Document "doc-1" belongs to a more recent version of Kibana [10.2.0] when the last known version is [10.1.0].' - ); - } - }); - it("doesn't throw error for documents using current version model when 'downwardConversion' is 'forbid'", async () => { - try { - await repositoryV2.get('test-type', 'doc-1', { - downwardConversion: 'forbid', - }); - } catch (err) { - expect(err).toBeUndefined(); - } - }); }); }); diff --git a/src/plugins/dashboard/kibana.jsonc b/src/plugins/dashboard/kibana.jsonc index c22d68173deb6..0f8601cb96c5d 100644 --- a/src/plugins/dashboard/kibana.jsonc +++ b/src/plugins/dashboard/kibana.jsonc @@ -34,7 +34,8 @@ "screenshotMode", "usageCollection", "taskManager", - "serverless" + "serverless", + "noDataPage" ], "requiredBundles": ["kibanaReact", "kibanaUtils", "presentationUtil"] } diff --git a/src/plugins/dashboard/public/dashboard_app/no_data/dashboard_app_no_data.tsx b/src/plugins/dashboard/public/dashboard_app/no_data/dashboard_app_no_data.tsx index fb04a3187c72c..8580ae2f168d3 100644 --- a/src/plugins/dashboard/public/dashboard_app/no_data/dashboard_app_no_data.tsx +++ b/src/plugins/dashboard/public/dashboard_app/no_data/dashboard_app_no_data.tsx @@ -26,6 +26,7 @@ export const DashboardAppNoDataPage = ({ http: { basePath }, documentationLinks: { indexPatternsDocLink, kibanaGuideDocLink }, customBranding, + noDataPage, } = pluginServices.getServices(); const analyticsServices = { @@ -44,6 +45,7 @@ export const DashboardAppNoDataPage = ({ }, dataViews, dataViewEditor, + noDataPage, }; return ( diff --git a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_table.tsx b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_table.tsx index 196fd04cddf6c..2386b414c3209 100644 --- a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_table.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_table.tsx @@ -34,6 +34,7 @@ export const DashboardListingTable = ({ getDashboardUrl, useSessionStorageIntegration, urlStateEnabled, + showCreateDashboardButton = true, }: DashboardListingProps) => { const { application, @@ -61,6 +62,7 @@ export const DashboardListingTable = ({ urlStateEnabled, useSessionStorageIntegration, initialFilter, + showCreateDashboardButton, }); const savedObjectsTaggingFakePlugin = useMemo( diff --git a/src/plugins/dashboard/public/dashboard_listing/hooks/use_dashboard_listing_table.test.tsx b/src/plugins/dashboard/public/dashboard_listing/hooks/use_dashboard_listing_table.test.tsx index 7716981e34942..602ac6a1f4a3c 100644 --- a/src/plugins/dashboard/public/dashboard_listing/hooks/use_dashboard_listing_table.test.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/hooks/use_dashboard_listing_table.test.tsx @@ -105,6 +105,22 @@ describe('useDashboardListingTable', () => { expect(result.current.unsavedDashboardIds).toEqual([]); }); + test('should not render the create dashboard button when showCreateDashboardButton is false', () => { + const initialFilter = 'myFilter'; + const { result } = renderHook(() => + useDashboardListingTable({ + getDashboardUrl, + goToDashboard, + initialFilter, + urlStateEnabled: false, + showCreateDashboardButton: false, + }) + ); + + const tableListViewTableProps = result.current.tableListViewTableProps; + expect(tableListViewTableProps.createItem).toBeUndefined(); + }); + test('should return the correct tableListViewTableProps', () => { const initialFilter = 'myFilter'; const { result } = renderHook(() => diff --git a/src/plugins/dashboard/public/dashboard_listing/hooks/use_dashboard_listing_table.tsx b/src/plugins/dashboard/public/dashboard_listing/hooks/use_dashboard_listing_table.tsx index aefa45500b450..8f2fb7ac76cc9 100644 --- a/src/plugins/dashboard/public/dashboard_listing/hooks/use_dashboard_listing_table.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/hooks/use_dashboard_listing_table.tsx @@ -70,6 +70,7 @@ export const useDashboardListingTable = ({ initialFilter, urlStateEnabled, useSessionStorageIntegration, + showCreateDashboardButton = true, }: { dashboardListingId?: string; disableCreateDashboardButton?: boolean; @@ -79,6 +80,7 @@ export const useDashboardListingTable = ({ initialFilter?: string; urlStateEnabled?: boolean; useSessionStorageIntegration?: boolean; + showCreateDashboardButton?: boolean; }): UseDashboardListingTableReturnType => { const { dashboardSessionStorage, @@ -274,7 +276,7 @@ export const useDashboardListingTable = ({ onSave: updateItemMeta, customValidators: contentEditorValidators, }, - createItem: !showWriteControls ? undefined : createItem, + createItem: !showWriteControls || !showCreateDashboardButton ? undefined : createItem, deleteItems: !showWriteControls ? undefined : deleteItems, editItem: !showWriteControls ? undefined : editItem, emptyPrompt, @@ -308,6 +310,7 @@ export const useDashboardListingTable = ({ initialPageSize, listingLimit, onFetchSuccess, + showCreateDashboardButton, showWriteControls, title, updateItemMeta, diff --git a/src/plugins/dashboard/public/dashboard_listing/types.ts b/src/plugins/dashboard/public/dashboard_listing/types.ts index c92344d4e778a..18767c1c75c32 100644 --- a/src/plugins/dashboard/public/dashboard_listing/types.ts +++ b/src/plugins/dashboard/public/dashboard_listing/types.ts @@ -17,6 +17,7 @@ export type DashboardListingProps = PropsWithChildren<{ goToDashboard: (dashboardId?: string, viewMode?: ViewMode) => void; getDashboardUrl: (dashboardId: string, usesTimeRestore: boolean) => string; urlStateEnabled?: boolean; + showCreateDashboardButton?: boolean; }>; // because the type of `application.capabilities.advancedSettings` is so generic, the provider diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index a28dbe9c45ae7..d2802a8ef3b6d 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -52,6 +52,7 @@ import type { DataPublicPluginSetup, DataPublicPluginStart } from '@kbn/data-plu import type { UrlForwardingSetup, UrlForwardingStart } from '@kbn/url-forwarding-plugin/public'; import type { SavedObjectTaggingOssPluginStart } from '@kbn/saved-objects-tagging-oss-plugin/public'; import type { ServerlessPluginStart } from '@kbn/serverless/public'; +import type { NoDataPagePluginStart } from '@kbn/no-data-page-plugin/public'; import { CustomBrandingStart } from '@kbn/core-custom-branding-browser'; import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; @@ -108,6 +109,7 @@ export interface DashboardStartDependencies { visualizations: VisualizationsStart; customBranding: CustomBrandingStart; serverless?: ServerlessPluginStart; + noDataPage?: NoDataPagePluginStart; } export interface DashboardSetup { diff --git a/src/plugins/dashboard/public/services/no_data_page/no_data_page_service.stub.ts b/src/plugins/dashboard/public/services/no_data_page/no_data_page_service.stub.ts new file mode 100644 index 0000000000000..c1af1452176a8 --- /dev/null +++ b/src/plugins/dashboard/public/services/no_data_page/no_data_page_service.stub.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; +import { NoDataPageService } from './types'; + +export type NoDataPageServiceFactory = PluginServiceFactory; + +export const noDataPageServiceFactory: NoDataPageServiceFactory = () => { + return { getAnalyticsNoDataPageFlavor: () => 'kibana' }; +}; diff --git a/src/plugins/dashboard/public/services/no_data_page/no_data_page_service.ts b/src/plugins/dashboard/public/services/no_data_page/no_data_page_service.ts new file mode 100644 index 0000000000000..f1dded3f12ff3 --- /dev/null +++ b/src/plugins/dashboard/public/services/no_data_page/no_data_page_service.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; +import { DashboardStartDependencies } from '../../plugin'; +import { NoDataPageService } from './types'; + +export type NoDataPageServiceFactory = KibanaPluginServiceFactory< + NoDataPageService, + DashboardStartDependencies +>; + +export const noDataPageServiceFactory: NoDataPageServiceFactory = ({ startPlugins }) => { + const { noDataPage } = startPlugins; + + return { + getAnalyticsNoDataPageFlavor: noDataPage?.getAnalyticsNoDataPageFlavor ?? (() => 'kibana'), + }; +}; diff --git a/src/plugins/dashboard/public/services/no_data_page/types.ts b/src/plugins/dashboard/public/services/no_data_page/types.ts new file mode 100644 index 0000000000000..7e87f1586db33 --- /dev/null +++ b/src/plugins/dashboard/public/services/no_data_page/types.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { NoDataPagePluginStart } from '@kbn/no-data-page-plugin/public'; + +export interface NoDataPageService { + getAnalyticsNoDataPageFlavor: NoDataPagePluginStart['getAnalyticsNoDataPageFlavor']; +} diff --git a/src/plugins/dashboard/public/services/plugin_services.stub.ts b/src/plugins/dashboard/public/services/plugin_services.stub.ts index 0ae4159ed2128..8ba55d486d75b 100644 --- a/src/plugins/dashboard/public/services/plugin_services.stub.ts +++ b/src/plugins/dashboard/public/services/plugin_services.stub.ts @@ -42,6 +42,7 @@ import { customBrandingServiceFactory } from './custom_branding/custom_branding. import { savedObjectsManagementServiceFactory } from './saved_objects_management/saved_objects_management_service.stub'; import { contentManagementServiceFactory } from './content_management/content_management_service.stub'; import { serverlessServiceFactory } from './serverless/serverless_service.stub'; +import { noDataPageServiceFactory } from './no_data_page/no_data_page_service.stub'; export const providers: PluginServiceProviders = { dashboardContentManagement: new PluginServiceProvider(dashboardContentManagementServiceFactory), @@ -72,6 +73,7 @@ export const providers: PluginServiceProviders = { savedObjectsManagement: new PluginServiceProvider(savedObjectsManagementServiceFactory), contentManagement: new PluginServiceProvider(contentManagementServiceFactory), serverless: new PluginServiceProvider(serverlessServiceFactory), + noDataPage: new PluginServiceProvider(noDataPageServiceFactory), }; export const registry = new PluginServiceRegistry(providers); diff --git a/src/plugins/dashboard/public/services/plugin_services.ts b/src/plugins/dashboard/public/services/plugin_services.ts index d84b55d0ff4a1..f16b4c8f34b0e 100644 --- a/src/plugins/dashboard/public/services/plugin_services.ts +++ b/src/plugins/dashboard/public/services/plugin_services.ts @@ -43,6 +43,7 @@ import { savedObjectsManagementServiceFactory } from './saved_objects_management import { dashboardContentManagementServiceFactory } from './dashboard_content_management/dashboard_content_management_service'; import { contentManagementServiceFactory } from './content_management/content_management_service'; import { serverlessServiceFactory } from './serverless/serverless_service'; +import { noDataPageServiceFactory } from './no_data_page/no_data_page_service'; const providers: PluginServiceProviders = { dashboardContentManagement: new PluginServiceProvider(dashboardContentManagementServiceFactory, [ @@ -86,6 +87,7 @@ const providers: PluginServiceProviders(); diff --git a/src/plugins/dashboard/public/services/types.ts b/src/plugins/dashboard/public/services/types.ts index 13adaf6098070..5ad3aab951121 100644 --- a/src/plugins/dashboard/public/services/types.ts +++ b/src/plugins/dashboard/public/services/types.ts @@ -38,6 +38,7 @@ import { DashboardUrlForwardingService } from './url_forwarding/types'; import { DashboardUsageCollectionService } from './usage_collection/types'; import { DashboardVisualizationsService } from './visualizations/types'; import { DashboardServerlessService } from './serverless/types'; +import { NoDataPageService } from './no_data_page/types'; export type DashboardPluginServiceParams = KibanaPluginServiceParams & { initContext: PluginInitializerContext; // need a custom type so that initContext is a required parameter for initializerContext @@ -72,4 +73,5 @@ export interface DashboardServices { savedObjectsManagement: SavedObjectsManagementPluginStart; contentManagement: ContentManagementPublicStart; serverless: DashboardServerlessService; // TODO: make this optional in follow up + noDataPage: NoDataPageService; } diff --git a/src/plugins/dashboard/tsconfig.json b/src/plugins/dashboard/tsconfig.json index 9d5dafa47c88a..31cc2b1aab236 100644 --- a/src/plugins/dashboard/tsconfig.json +++ b/src/plugins/dashboard/tsconfig.json @@ -64,7 +64,8 @@ "@kbn/content-management-table-list-view-table", "@kbn/shared-ux-prompt-not-found", "@kbn/content-management-content-editor", - "@kbn/serverless" + "@kbn/serverless", + "@kbn/no-data-page-plugin" ], "exclude": ["target/**/*"] } diff --git a/src/plugins/data/common/search/search_source/search_source.test.ts b/src/plugins/data/common/search/search_source/search_source.test.ts index eeb4c5a972e3a..8ceaab7e12a18 100644 --- a/src/plugins/data/common/search/search_source/search_source.test.ts +++ b/src/plugins/data/common/search/search_source/search_source.test.ts @@ -1105,32 +1105,6 @@ describe('SearchSource', () => { expect(complete2).toBeCalledTimes(1); expect(searchSourceDependencies.search).toHaveBeenCalledTimes(1); }); - - test('should emit error on empty response', async () => { - searchSourceDependencies.search = mockSearchMethod = jest - .fn() - .mockReturnValue( - of({ rawResponse: { test: 1 }, isPartial: true, isRunning: true }, undefined) - ); - - searchSource = new SearchSource({ index: indexPattern }, searchSourceDependencies); - const options = {}; - - const next = jest.fn(); - const error = jest.fn(); - const complete = jest.fn(); - const res$ = searchSource.fetch$(options); - res$.subscribe({ next, error, complete }); - await firstValueFrom(res$).catch((_) => {}); - - expect(next).toBeCalledTimes(1); - expect(error).toBeCalledTimes(1); - expect(complete).toBeCalledTimes(0); - expect(next.mock.calls[0][0].rawResponse).toStrictEqual({ - test: 1, - }); - expect(error.mock.calls[0][0]).toBe(undefined); - }); }); describe('inspector', () => { diff --git a/src/plugins/data/common/search/search_source/search_source.ts b/src/plugins/data/common/search/search_source/search_source.ts index c11289439885b..91eea3e50f881 100644 --- a/src/plugins/data/common/search/search_source/search_source.ts +++ b/src/plugins/data/common/search/search_source/search_source.ts @@ -106,7 +106,6 @@ import { getRequestInspectorStats, getResponseInspectorStats } from './inspect'; import { getEsQueryConfig, IKibanaSearchResponse, - isErrorResponse, isPartialResponse, isCompleteResponse, UI_SETTINGS, @@ -547,9 +546,7 @@ export class SearchSource { // For testing timeout messages in UI, uncomment the next line // response.rawResponse.timed_out = true; return new Observable>((obs) => { - if (isErrorResponse(response)) { - obs.error(response); - } else if (isPartialResponse(response)) { + if (isPartialResponse(response)) { obs.next(this.postFlightTransform(response)); } else { if (!this.hasPostFlightRequests()) { diff --git a/src/plugins/data/public/mocks.ts b/src/plugins/data/public/mocks.ts index fadc844af4b58..e26bb07268b6d 100644 --- a/src/plugins/data/public/mocks.ts +++ b/src/plugins/data/public/mocks.ts @@ -33,6 +33,7 @@ const createStartContract = (): Start => { actions: { createFiltersFromValueClickAction: jest.fn().mockResolvedValue(['yes']), createFiltersFromRangeSelectAction: jest.fn(), + createFiltersFromMultiValueClickAction: jest.fn(), }, datatableUtilities: createDatatableUtilitiesMock(), search: searchServiceMock.createStartContract(), diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts index a2ed53fde7704..f3679aa628560 100644 --- a/src/plugins/data/public/plugin.ts +++ b/src/plugins/data/public/plugin.ts @@ -34,6 +34,7 @@ import { import { createFiltersFromValueClickAction, createFiltersFromRangeSelectAction, + createFiltersFromMultiValueClickAction, createMultiValueClickActionDefinition, createValueClickActionDefinition, createSelectRangeActionDefinition, @@ -168,6 +169,7 @@ export class DataPublicPlugin actions: { createFiltersFromValueClickAction, createFiltersFromRangeSelectAction, + createFiltersFromMultiValueClickAction, }, datatableUtilities, fieldFormats, diff --git a/src/plugins/data/public/types.ts b/src/plugins/data/public/types.ts index fe803b76364d8..924ddc87fb426 100644 --- a/src/plugins/data/public/types.ts +++ b/src/plugins/data/public/types.ts @@ -17,7 +17,11 @@ import { ScreenshotModePluginStart } from '@kbn/screenshot-mode-plugin/public'; import { SharePluginStart } from '@kbn/share-plugin/public'; import { ManagementSetup } from '@kbn/management-plugin/public'; import { DatatableUtilitiesService } from '../common'; -import { createFiltersFromRangeSelectAction, createFiltersFromValueClickAction } from './actions'; +import { + createFiltersFromMultiValueClickAction, + createFiltersFromRangeSelectAction, + createFiltersFromValueClickAction, +} from './actions'; import type { ISearchSetup, ISearchStart } from './search'; import { QuerySetup, QueryStart } from './query'; import { DataViewsContract } from './data_views'; @@ -55,6 +59,7 @@ export interface DataPublicPluginSetup { export interface DataPublicPluginStartActions { createFiltersFromValueClickAction: typeof createFiltersFromValueClickAction; createFiltersFromRangeSelectAction: typeof createFiltersFromRangeSelectAction; + createFiltersFromMultiValueClickAction: typeof createFiltersFromMultiValueClickAction; } /** diff --git a/src/plugins/data_view_editor/public/components/data_view_editor_flyout_content.tsx b/src/plugins/data_view_editor/public/components/data_view_editor_flyout_content.tsx index 42614d33b660a..ad344d482c0cf 100644 --- a/src/plugins/data_view_editor/public/components/data_view_editor_flyout_content.tsx +++ b/src/plugins/data_view_editor/public/components/data_view_editor_flyout_content.tsx @@ -231,7 +231,11 @@ const IndexPatternEditorFlyoutContentComponent = ({ return ( - +

    {editData ? editorTitleEditMode : editorTitle}

    diff --git a/src/plugins/discover/kibana.jsonc b/src/plugins/discover/kibana.jsonc index 1c6ffaae833cb..3da4912dd5661 100644 --- a/src/plugins/discover/kibana.jsonc +++ b/src/plugins/discover/kibana.jsonc @@ -26,7 +26,7 @@ "expressions", "unifiedSearch", "unifiedHistogram", - "contentManagement", + "contentManagement" ], "optionalPlugins": [ "home", @@ -36,7 +36,8 @@ "triggersActionsUi", "savedObjectsTaggingOss", "lens", - "serverless" + "serverless", + "noDataPage" ], "requiredBundles": ["kibanaUtils", "kibanaReact", "unifiedSearch"], "extraPublicDirs": ["common"] diff --git a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx index a378d25fb04de..10c0ca87ce504 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx @@ -297,7 +297,13 @@ function DiscoverDocumentsComponent({ )} {isDataLoading && ( - + )} ); diff --git a/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.test.tsx b/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.test.tsx index e4a2a674c0f77..53e35d207507c 100644 --- a/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.test.tsx +++ b/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.test.tsx @@ -25,6 +25,9 @@ import { import { createMockUnifiedHistogramApi } from '@kbn/unified-histogram-plugin/public/mocks'; import { checkHitCount, sendErrorTo } from '../../hooks/use_saved_search_messages'; import type { InspectorAdapters } from '../../hooks/use_inspector'; +import { UnifiedHistogramCustomization } from '../../../../customizations/customization_types/histogram_customization'; +import { useDiscoverCustomization } from '../../../../customizations'; +import { DiscoverCustomizationId } from '../../../../customizations/customization_service'; const mockData = dataPluginMock.createStartContract(); let mockQueryState = { @@ -71,6 +74,19 @@ jest.mock('../../hooks/use_saved_search_messages', () => { sendErrorTo: jest.fn(originalModule.sendErrorTo), }; }); +jest.mock('../../../../customizations', () => ({ + ...jest.requireActual('../../../../customizations'), + useDiscoverCustomization: jest.fn(), +})); + +let mockUseCustomizations = false; + +const mockHistogramCustomization: UnifiedHistogramCustomization = { + id: 'unified_histogram', + onFilter: jest.fn(), + onBrushEnd: jest.fn(), + withDefaultActions: true, +}; const mockCheckHitCount = checkHitCount as jest.MockedFunction; @@ -126,6 +142,23 @@ describe('useDiscoverHistogram', () => { return { hook, initialProps }; }; + beforeEach(() => { + mockUseCustomizations = false; + jest.clearAllMocks(); + + (useDiscoverCustomization as jest.Mock).mockImplementation((id: DiscoverCustomizationId) => { + if (!mockUseCustomizations) { + return undefined; + } + switch (id) { + case 'unified_histogram': + return mockHistogramCustomization; + default: + throw new Error(`Unknown customization id: ${id}`); + } + }); + }); + describe('initialization', () => { it('should return the expected parameters from getCreationOptions', async () => { const { hook } = await renderUseDiscoverHistogram(); @@ -447,4 +480,19 @@ describe('useDiscoverHistogram', () => { expect(api.refetch).toHaveBeenCalled(); }); }); + + describe('customization', () => { + test('should use custom values provided by customization fwk ', async () => { + mockUseCustomizations = true; + const stateContainer = getStateContainer(); + const { hook } = await renderUseDiscoverHistogram({ stateContainer }); + + expect(hook.result.current.onFilter).toEqual(mockHistogramCustomization.onFilter); + expect(hook.result.current.onBrushEnd).toEqual(mockHistogramCustomization.onBrushEnd); + expect(hook.result.current.withDefaultActions).toEqual( + mockHistogramCustomization.withDefaultActions + ); + expect(hook.result.current.disabledActions).toBeUndefined(); + }); + }); }); diff --git a/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.ts b/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.ts index 14144634ff28c..68d821580f5a4 100644 --- a/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.ts +++ b/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.ts @@ -26,6 +26,7 @@ import { } from 'rxjs'; import useObservable from 'react-use/lib/useObservable'; import type { RequestAdapter } from '@kbn/inspector-plugin/common'; +import { useDiscoverCustomization } from '../../../../customizations'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; import { getUiActions } from '../../../../kibana_services'; import { FetchStatus } from '../../../types'; @@ -302,6 +303,8 @@ export const useDiscoverHistogram = ({ const dataView = useInternalStateSelector((state) => state.dataView!); + const histogramCustomization = useDiscoverCustomization('unified_histogram'); + return { ref, getCreationOptions, @@ -312,6 +315,10 @@ export const useDiscoverHistogram = ({ timeRange, relativeTimeRange, columns, + onFilter: histogramCustomization?.onFilter, + onBrushEnd: histogramCustomization?.onBrushEnd, + withDefaultActions: histogramCustomization?.withDefaultActions, + disabledActions: histogramCustomization?.disabledActions, }; }; diff --git a/src/plugins/discover/public/application/main/discover_main_route.tsx b/src/plugins/discover/public/application/main/discover_main_route.tsx index cf62f96ca7d1a..bd8a5d3454602 100644 --- a/src/plugins/discover/public/application/main/discover_main_route.tsx +++ b/src/plugins/discover/public/application/main/discover_main_route.tsx @@ -48,11 +48,7 @@ export interface MainRouteProps { mode?: DiscoverDisplayMode; } -export function DiscoverMainRoute({ - customizationCallbacks, - isDev, - mode = 'standalone', -}: MainRouteProps) { +export function DiscoverMainRoute({ customizationCallbacks, mode = 'standalone' }: MainRouteProps) { const history = useHistory(); const services = useDiscoverServices(); const { @@ -64,10 +60,12 @@ export function DiscoverMainRoute({ dataViewEditor, } = services; const { id: savedSearchId } = useParams(); + const stateContainer = useSingleton(() => getDiscoverStateContainer({ history, services, + mode, }) ); const { customizationService, isInitialized: isCustomizationServiceInitialized } = @@ -109,7 +107,7 @@ export function DiscoverMainRoute({ const hasUserDataViewValue = await data.dataViews.hasData .hasUserDataView() .catch(() => false); - const hasESDataValue = isDev || (await data.dataViews.hasData.hasESData().catch(() => false)); + const hasESDataValue = await data.dataViews.hasData.hasESData().catch(() => false); setHasUserDataView(hasUserDataViewValue); setHasESData(hasESDataValue); @@ -134,7 +132,7 @@ export function DiscoverMainRoute({ setError(e); return false; } - }, [data.dataViews, isDev, savedSearchId]); + }, [data.dataViews, savedSearchId]); const loadSavedSearch = useCallback( async (nextDataView?: DataView) => { @@ -256,11 +254,12 @@ export function DiscoverMainRoute({ // We've already called this, so we can optimize the analytics services to // use the already-retrieved data to avoid a double-call. - hasESData: () => Promise.resolve(isDev ? true : hasESData), + hasESData: () => Promise.resolve(hasESData), hasUserDataView: () => Promise.resolve(hasUserDataView), }, }, dataViewEditor, + noDataPage: services.noDataPage, }; return ( diff --git a/src/plugins/discover/public/application/main/services/discover_state.test.ts b/src/plugins/discover/public/application/main/services/discover_state.test.ts index 6533fd74c1fce..f402988942a10 100644 --- a/src/plugins/discover/public/application/main/services/discover_state.test.ts +++ b/src/plugins/discover/public/application/main/services/discover_state.test.ts @@ -737,3 +737,43 @@ describe('Test discover state actions', () => { expect(setRefreshInterval).toHaveBeenCalledWith({ pause: false, value: 1000 }); }); }); + +describe('Test discover state with embedded mode', () => { + let stopSync = () => {}; + let history: History; + let state: DiscoverStateContainer; + const getCurrentUrl = () => history.createHref(history.location); + + beforeEach(async () => { + history = createBrowserHistory(); + history.push('/'); + state = getDiscoverStateContainer({ + services: discoverServiceMock, + history, + mode: 'embedded', + }); + state.savedSearchState.set(savedSearchMock); + await state.appState.update({}, true); + stopSync = startSync(state.appState); + }); + afterEach(() => { + stopSync(); + stopSync = () => {}; + }); + test('setting app state and syncing to URL', async () => { + state.appState.update({ index: 'modified' }); + await new Promise(process.nextTick); + expect(getCurrentUrl()).toMatchInlineSnapshot( + `"/?_a=(columns:!(default_column),index:modified,interval:auto,sort:!())"` + ); + }); + + test('changing URL to be propagated to appState', async () => { + history.push('/?_a=(index:modified)'); + expect(state.appState.getState()).toMatchObject( + expect.objectContaining({ + index: 'modified', + }) + ); + }); +}); diff --git a/src/plugins/discover/public/application/main/services/discover_state.ts b/src/plugins/discover/public/application/main/services/discover_state.ts index 1a34f097f3f4e..d6e2809c14bd6 100644 --- a/src/plugins/discover/public/application/main/services/discover_state.ts +++ b/src/plugins/discover/public/application/main/services/discover_state.ts @@ -26,7 +26,7 @@ import { merge } from 'rxjs'; import { AggregateQuery, Query, TimeRange } from '@kbn/es-query'; import { loadSavedSearch as loadSavedSearchFn } from './load_saved_search'; import { restoreStateFromSavedSearch } from '../../../services/saved_searches/restore_from_saved_search'; -import { FetchStatus } from '../../types'; +import { DiscoverDisplayMode, FetchStatus } from '../../types'; import { changeDataView } from '../hooks/utils/change_data_view'; import { buildStateSubscribe } from '../hooks/utils/build_state_subscribe'; import { addLog } from '../../../utils/add_log'; @@ -64,6 +64,11 @@ interface DiscoverStateContainerParams { * core ui settings service */ services: DiscoverServices; + /* + * mode in which discover is running + * + * */ + mode?: DiscoverDisplayMode; } export interface LoadParams { @@ -188,6 +193,7 @@ export interface DiscoverStateContainer { export function getDiscoverStateContainer({ history, services, + mode = 'standalone', }: DiscoverStateContainerParams): DiscoverStateContainer { const storeInSessionStorage = services.uiSettings.get('state:storeInSessionStorage'); const toasts = services.core.notifications.toasts; @@ -198,6 +204,7 @@ export function getDiscoverStateContainer({ const stateStorage = createKbnUrlStateStorage({ useHash: storeInSessionStorage, history, + useHashQuery: mode !== 'embedded', ...(toasts && withNotifyOnErrors(toasts)), }); diff --git a/src/plugins/discover/public/build_services.ts b/src/plugins/discover/public/build_services.ts index 254292e6d07e6..b903d2485bdc4 100644 --- a/src/plugins/discover/public/build_services.ts +++ b/src/plugins/discover/public/build_services.ts @@ -7,7 +7,6 @@ */ import { History } from 'history'; -import { memoize } from 'lodash'; import { Capabilities, @@ -52,7 +51,9 @@ import type { LensPublicStart } from '@kbn/lens-plugin/public'; import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; import type { SettingsStart } from '@kbn/core-ui-settings-browser'; import type { ContentClient } from '@kbn/content-management-plugin/public'; +import { memoize } from 'lodash'; import type { ServerlessPluginStart } from '@kbn/serverless/public'; +import type { NoDataPagePluginStart } from '@kbn/no-data-page-plugin/public'; import { getHistory } from './kibana_services'; import { DiscoverStartPlugins } from './plugin'; import { DiscoverContextAppLocator } from './application/context/services/locator'; @@ -111,6 +112,7 @@ export interface DiscoverServices { uiActions: UiActionsStart; contentClient: ContentClient; serverless?: ServerlessPluginStart; + noDataPage?: NoDataPagePluginStart; } export const buildServices = memoize(function ( @@ -171,5 +173,6 @@ export const buildServices = memoize(function ( uiActions: plugins.uiActions, contentClient: plugins.contentManagement.client, serverless: plugins.serverless, + noDataPage: plugins.noDataPage, }; }); diff --git a/src/plugins/discover/public/components/discover_container/discover_container.test.tsx b/src/plugins/discover/public/components/discover_container/discover_container.test.tsx index 9b963f69ccf84..708fc93e63b81 100644 --- a/src/plugins/discover/public/components/discover_container/discover_container.test.tsx +++ b/src/plugins/discover/public/components/discover_container/discover_container.test.tsx @@ -39,7 +39,7 @@ const TestComponent = (props: Partial) => { return ( )} getDiscoverServices={getDiscoverServicesMock} diff --git a/src/plugins/discover/public/components/discover_container/discover_container.tsx b/src/plugins/discover/public/components/discover_container/discover_container.tsx index 6cd70ff430255..28a596946bd18 100644 --- a/src/plugins/discover/public/components/discover_container/discover_container.tsx +++ b/src/plugins/discover/public/components/discover_container/discover_container.tsx @@ -27,8 +27,9 @@ export interface DiscoverContainerInternalProps { overrideServices: Partial; getDiscoverServices: () => Promise; scopedHistory: ScopedHistory; - customize: CustomizationCallback; + customizationCallbacks: CustomizationCallback[]; isDev: boolean; + isLoading?: boolean; } const discoverContainerWrapperCss = css` @@ -45,12 +46,12 @@ const discoverContainerWrapperCss = css` export const DiscoverContainerInternal = ({ overrideServices, scopedHistory, - customize, + customizationCallbacks, isDev, getDiscoverServices, + isLoading = false, }: DiscoverContainerInternalProps) => { const [discoverServices, setDiscoverServices] = useState(); - const customizationCallbacks = useMemo(() => [customize], [customize]); const [initialized, setInitialized] = useState(false); useEffect(() => { @@ -68,7 +69,7 @@ export const DiscoverContainerInternal = ({ return { ...discoverServices, ...overrideServices }; }, [discoverServices, overrideServices]); - if (!initialized || !services) { + if (!initialized || !services || isLoading) { return ( diff --git a/src/plugins/discover/public/components/discover_grid/discover_grid.tsx b/src/plugins/discover/public/components/discover_grid/discover_grid.tsx index 414b8986e5cbc..c9ef3119ae421 100644 --- a/src/plugins/discover/public/components/discover_grid/discover_grid.tsx +++ b/src/plugins/discover/public/components/discover_grid/discover_grid.tsx @@ -621,7 +621,7 @@ export const DiscoverGrid = ({ if (!rowCount && loadingState === DataLoadingState.loading) { return ( -
    +
    diff --git a/src/plugins/discover/public/customizations/customization_service.ts b/src/plugins/discover/public/customizations/customization_service.ts index 4a9b9bf2588af..0b57ba37e07dc 100644 --- a/src/plugins/discover/public/customizations/customization_service.ts +++ b/src/plugins/discover/public/customizations/customization_service.ts @@ -7,9 +7,16 @@ */ import { filter, map, Observable, startWith, Subject } from 'rxjs'; -import type { SearchBarCustomization, TopNavCustomization } from './customization_types'; +import type { + SearchBarCustomization, + TopNavCustomization, + UnifiedHistogramCustomization, +} from './customization_types'; -export type DiscoverCustomization = SearchBarCustomization | TopNavCustomization; +export type DiscoverCustomization = + | SearchBarCustomization + | TopNavCustomization + | UnifiedHistogramCustomization; export type DiscoverCustomizationId = DiscoverCustomization['id']; diff --git a/src/plugins/discover/public/customizations/customization_types/histogram_customization.tsx b/src/plugins/discover/public/customizations/customization_types/histogram_customization.tsx new file mode 100644 index 0000000000000..73313b2f37ba9 --- /dev/null +++ b/src/plugins/discover/public/customizations/customization_types/histogram_customization.tsx @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { UnifiedHistogramContainerProps } from '@kbn/unified-histogram-plugin/public'; + +interface UnifiedHistogramCustomizationId { + id: 'unified_histogram'; +} + +export type UnifiedHistogramCustomization = UnifiedHistogramCustomizationId & + Pick< + UnifiedHistogramContainerProps, + 'onFilter' | 'onBrushEnd' | 'withDefaultActions' | 'disabledActions' + >; diff --git a/src/plugins/discover/public/customizations/customization_types/index.ts b/src/plugins/discover/public/customizations/customization_types/index.ts index e7ea9591f8ad4..e920a68574b93 100644 --- a/src/plugins/discover/public/customizations/customization_types/index.ts +++ b/src/plugins/discover/public/customizations/customization_types/index.ts @@ -8,3 +8,4 @@ export * from './search_bar_customization'; export * from './top_nav_customization'; +export * from './histogram_customization'; diff --git a/src/plugins/discover/public/customizations/index.ts b/src/plugins/discover/public/customizations/index.ts index bc0a7e03525c9..0c9a94ac8adad 100644 --- a/src/plugins/discover/public/customizations/index.ts +++ b/src/plugins/discover/public/customizations/index.ts @@ -9,3 +9,4 @@ export * from './customization_types'; export * from './customization_provider'; export * from './types'; +export type { DiscoverCustomization, DiscoverCustomizationService } from './customization_service'; diff --git a/src/plugins/discover/public/index.ts b/src/plugins/discover/public/index.ts index f567380276354..5af7c2bf1142a 100644 --- a/src/plugins/discover/public/index.ts +++ b/src/plugins/discover/public/index.ts @@ -22,6 +22,11 @@ export type { DiscoverProfileId, DiscoverProfileOptions, RegisterCustomizationProfile, + DiscoverCustomization, + DiscoverCustomizationService, + SearchBarCustomization, + UnifiedHistogramCustomization, + TopNavCustomization, } from './customizations'; export { SEARCH_EMBEDDABLE_TYPE, SEARCH_EMBEDDABLE_CELL_ACTIONS_TRIGGER_ID } from './embeddable'; export { loadSharingDataHelpers } from './utils'; diff --git a/src/plugins/discover/public/plugin.tsx b/src/plugins/discover/public/plugin.tsx index 6db46f53c35fb..c3b92275d3956 100644 --- a/src/plugins/discover/public/plugin.tsx +++ b/src/plugins/discover/public/plugin.tsx @@ -46,6 +46,7 @@ import { setStateToKbnUrl } from '@kbn/kibana-utils-plugin/public'; import type { LensPublicStart } from '@kbn/lens-plugin/public'; import type { ServerlessPluginStart } from '@kbn/serverless/public'; import { DOC_TABLE_LEGACY, TRUNCATE_MAX_HEIGHT } from '@kbn/discover-utils'; +import { NoDataPagePluginStart } from '@kbn/no-data-page-plugin/public'; import { PLUGIN_ID } from '../common'; import { DocViewInput, DocViewInputFn } from './services/doc_views/doc_views_types'; import { DocViewsRegistry } from './services/doc_views/doc_views_registry'; @@ -213,6 +214,7 @@ export interface DiscoverStartPlugins { lens: LensPublicStart; contentManagement: ContentManagementPublicStart; serverless?: ServerlessPluginStart; + noDataPage?: NoDataPagePluginStart; } /** diff --git a/src/plugins/discover/tsconfig.json b/src/plugins/discover/tsconfig.json index df92ba4c9070c..8e93218385709 100644 --- a/src/plugins/discover/tsconfig.json +++ b/src/plugins/discover/tsconfig.json @@ -70,7 +70,8 @@ "@kbn/content-management-plugin", "@kbn/serverless", "@kbn/react-kibana-mount", - "@kbn/react-kibana-context-render" + "@kbn/react-kibana-context-render", + "@kbn/no-data-page-plugin" ], "exclude": [ "target/**/*" diff --git a/src/plugins/kibana_react/public/code_editor/__snapshots__/code_editor.test.tsx.snap b/src/plugins/kibana_react/public/code_editor/__snapshots__/code_editor.test.tsx.snap index bfbc661cad9b0..0a3ba18656cae 100644 --- a/src/plugins/kibana_react/public/code_editor/__snapshots__/code_editor.test.tsx.snap +++ b/src/plugins/kibana_react/public/code_editor/__snapshots__/code_editor.test.tsx.snap @@ -126,6 +126,7 @@ exports[` is rendered 1`] = ` >
    = ({ const { CopyButton } = useCopy({ isCopyable, value }); return ( -
    +
    {renderPrompt()} diff --git a/src/plugins/no_data_page/README.md b/src/plugins/no_data_page/README.md new file mode 100755 index 0000000000000..a516e3274ff3f --- /dev/null +++ b/src/plugins/no_data_page/README.md @@ -0,0 +1,3 @@ +# No Data Page + +Helps to globally configure the no data page components diff --git a/src/plugins/no_data_page/config.ts b/src/plugins/no_data_page/config.ts new file mode 100644 index 0000000000000..8fae1aad10aaa --- /dev/null +++ b/src/plugins/no_data_page/config.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { schema, TypeOf, offeringBasedSchema } from '@kbn/config-schema'; + +export const configSchema = schema.object({ + analyticsNoDataPageFlavor: offeringBasedSchema({ + serverless: schema.oneOf( + [schema.oneOf([schema.literal('kibana'), schema.literal('serverless_search')])], + { defaultValue: 'kibana' as const } + ), + }), +}); +export type NoDataPageConfig = TypeOf; diff --git a/src/plugins/no_data_page/jest.config.js b/src/plugins/no_data_page/jest.config.js new file mode 100644 index 0000000000000..546031bc12414 --- /dev/null +++ b/src/plugins/no_data_page/jest.config.js @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/no_data_page'], + coverageDirectory: '/target/kibana-coverage/jest/src/plugins/no_data_page', + coverageReporters: ['text', 'html'], + collectCoverageFrom: ['/src/plugins/no_data_page/{common,public,server}/**/*.{ts,tsx}'], +}; diff --git a/src/plugins/no_data_page/kibana.jsonc b/src/plugins/no_data_page/kibana.jsonc new file mode 100644 index 0000000000000..202917173b7a4 --- /dev/null +++ b/src/plugins/no_data_page/kibana.jsonc @@ -0,0 +1,10 @@ +{ + "type": "plugin", + "id": "@kbn/no-data-page-plugin", + "owner": "@elastic/appex-sharedux", + "plugin": { + "id": "noDataPage", + "server": true, + "browser": true + } +} diff --git a/src/plugins/no_data_page/public/index.ts b/src/plugins/no_data_page/public/index.ts new file mode 100644 index 0000000000000..28dfcd6044403 --- /dev/null +++ b/src/plugins/no_data_page/public/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { PluginInitializerContext } from '@kbn/core-plugins-browser'; +import { NoDataPagePlugin } from './plugin'; + +export function plugin(ctx: PluginInitializerContext) { + return new NoDataPagePlugin(ctx); +} + +export type { NoDataPagePluginSetup, NoDataPagePluginStart } from './types'; diff --git a/src/plugins/no_data_page/public/plugin.ts b/src/plugins/no_data_page/public/plugin.ts new file mode 100644 index 0000000000000..740f796f4f395 --- /dev/null +++ b/src/plugins/no_data_page/public/plugin.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/core/public'; +import type { NoDataPagePluginSetup, NoDataPagePluginStart } from './types'; +import type { NoDataPageConfig } from '../config'; + +export class NoDataPagePlugin implements Plugin { + constructor(private initializerContext: PluginInitializerContext) {} + + public setup(core: CoreSetup): NoDataPagePluginSetup { + return { + getAnalyticsNoDataPageFlavor: () => { + return this.initializerContext.config.get().analyticsNoDataPageFlavor; + }, + }; + } + + public start(core: CoreStart): NoDataPagePluginStart { + return { + getAnalyticsNoDataPageFlavor: () => { + return this.initializerContext.config.get().analyticsNoDataPageFlavor; + }, + }; + } +} diff --git a/src/plugins/no_data_page/public/types.ts b/src/plugins/no_data_page/public/types.ts new file mode 100644 index 0000000000000..c9523f7fcd93a --- /dev/null +++ b/src/plugins/no_data_page/public/types.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export interface NoDataPagePluginSetup { + getAnalyticsNoDataPageFlavor: () => 'kibana' | 'serverless_search'; +} + +export type NoDataPagePluginStart = NoDataPagePluginSetup; diff --git a/src/plugins/no_data_page/server/index.ts b/src/plugins/no_data_page/server/index.ts new file mode 100644 index 0000000000000..ba02a016a9676 --- /dev/null +++ b/src/plugins/no_data_page/server/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { PluginConfigDescriptor } from '@kbn/core-plugins-server'; + +import { configSchema, NoDataPageConfig } from '../config'; + +export const config: PluginConfigDescriptor = { + exposeToBrowser: { + analyticsNoDataPageFlavor: true, + }, + schema: configSchema, +}; + +export function plugin() { + return new (class NoDataPagePlugin { + setup() {} + start() {} + })(); +} diff --git a/src/plugins/no_data_page/tsconfig.json b/src/plugins/no_data_page/tsconfig.json new file mode 100644 index 0000000000000..bab1c8c23edfb --- /dev/null +++ b/src/plugins/no_data_page/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types" + }, + "include": ["common/**/*", "public/**/*", "server/**/*", "config.ts"], + "kbn_references": [ + "@kbn/core", + "@kbn/core-plugins-browser", + "@kbn/core-plugins-server", + "@kbn/config-schema", + ], + "exclude": ["target/**/*"] +} diff --git a/src/plugins/unified_histogram/public/chart/chart.test.tsx b/src/plugins/unified_histogram/public/chart/chart.test.tsx index 640ca91c0bc89..3ce4f829ebac3 100644 --- a/src/plugins/unified_histogram/public/chart/chart.test.tsx +++ b/src/plugins/unified_histogram/public/chart/chart.test.tsx @@ -103,6 +103,7 @@ async function mountComponent({ onResetChartHeight: jest.fn(), onChartHiddenChange: jest.fn(), onTimeIntervalChange: jest.fn(), + withDefaultActions: undefined, }; let instance: ReactWrapper = {} as ReactWrapper; diff --git a/src/plugins/unified_histogram/public/chart/chart.tsx b/src/plugins/unified_histogram/public/chart/chart.tsx index ba6ffbaf149fe..a9baa653ecee4 100644 --- a/src/plugins/unified_histogram/public/chart/chart.tsx +++ b/src/plugins/unified_histogram/public/chart/chart.tsx @@ -16,7 +16,7 @@ import { EuiToolTip, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import type { Suggestion } from '@kbn/lens-plugin/public'; +import type { EmbeddableComponentProps, Suggestion } from '@kbn/lens-plugin/public'; import type { Datatable } from '@kbn/expressions-plugin/common'; import { DataView, DataViewField, DataViewType } from '@kbn/data-views-plugin/public'; import type { LensEmbeddableInput } from '@kbn/lens-plugin/public'; @@ -78,6 +78,7 @@ export interface ChartProps { onChartLoad?: (event: UnifiedHistogramChartLoadEvent) => void; onFilter?: LensEmbeddableInput['onFilter']; onBrushEnd?: LensEmbeddableInput['onBrushEnd']; + withDefaultActions: EmbeddableComponentProps['withDefaultActions']; } const HistogramMemoized = memo(Histogram); @@ -113,6 +114,7 @@ export function Chart({ onChartLoad, onFilter, onBrushEnd, + withDefaultActions, }: ChartProps) { const [isSaveModalVisible, setIsSaveModalVisible] = useState(false); const [isFlyoutVisible, setIsFlyoutVisible] = useState(false); @@ -429,6 +431,7 @@ export function Chart({ onChartLoad={onChartLoad} onFilter={onFilter} onBrushEnd={onBrushEnd} + withDefaultActions={withDefaultActions} /> {appendHistogram} diff --git a/src/plugins/unified_histogram/public/chart/histogram.test.tsx b/src/plugins/unified_histogram/public/chart/histogram.test.tsx index 4c651f0b5e391..ef77af13209c0 100644 --- a/src/plugins/unified_histogram/public/chart/histogram.test.tsx +++ b/src/plugins/unified_histogram/public/chart/histogram.test.tsx @@ -70,6 +70,7 @@ function mountComponent() { lensAttributesContext: getMockLensAttributes(), onTotalHitsChange: jest.fn(), onChartLoad: jest.fn(), + withDefaultActions: undefined, }; return { @@ -95,16 +96,18 @@ describe('Histogram', () => { attributes: getMockLensAttributes().attributes, onLoad: lensProps.onLoad, }); - expect(lensProps).toEqual(originalProps); + expect(lensProps).toMatchObject(expect.objectContaining(originalProps)); component.setProps({ request: { ...props.request, searchSessionId: '321' } }).update(); lensProps = component.find(embeddable).props(); - expect(lensProps).toEqual(originalProps); + expect(lensProps).toMatchObject(expect.objectContaining(originalProps)); await act(async () => { props.refetch$.next({ type: 'refetch' }); }); component.update(); lensProps = component.find(embeddable).props(); - expect(lensProps).toEqual({ ...originalProps, searchSessionId: '321' }); + expect(lensProps).toMatchObject( + expect.objectContaining({ ...originalProps, searchSessionId: '321' }) + ); }); it('should execute onLoad correctly', async () => { diff --git a/src/plugins/unified_histogram/public/chart/histogram.tsx b/src/plugins/unified_histogram/public/chart/histogram.tsx index 761e701e8f9a6..ffc9cb82515a5 100644 --- a/src/plugins/unified_histogram/public/chart/histogram.tsx +++ b/src/plugins/unified_histogram/public/chart/histogram.tsx @@ -14,7 +14,7 @@ import type { DefaultInspectorAdapters } from '@kbn/expressions-plugin/common'; import type { IKibanaSearchResponse } from '@kbn/data-plugin/public'; import type { estypes } from '@elastic/elasticsearch'; import type { TimeRange } from '@kbn/es-query'; -import type { LensEmbeddableInput } from '@kbn/lens-plugin/public'; +import type { EmbeddableComponentProps, LensEmbeddableInput } from '@kbn/lens-plugin/public'; import { RequestStatus } from '@kbn/inspector-plugin/public'; import type { Observable } from 'rxjs'; import { @@ -50,6 +50,7 @@ export interface HistogramProps { onChartLoad?: (event: UnifiedHistogramChartLoadEvent) => void; onFilter?: LensEmbeddableInput['onFilter']; onBrushEnd?: LensEmbeddableInput['onBrushEnd']; + withDefaultActions: EmbeddableComponentProps['withDefaultActions']; } export function Histogram({ @@ -69,6 +70,7 @@ export function Histogram({ onChartLoad, onFilter, onBrushEnd, + withDefaultActions, }: HistogramProps) { const [bucketInterval, setBucketInterval] = useState(); const [chartSize, setChartSize] = useState('100%'); @@ -189,6 +191,7 @@ export function Histogram({ disabledActions={disabledActions} onFilter={onFilter} onBrushEnd={onBrushEnd} + withDefaultActions={withDefaultActions} />
    {timeRangeDisplay} diff --git a/src/plugins/unified_histogram/public/container/container.tsx b/src/plugins/unified_histogram/public/container/container.tsx index 0633e9d710966..311a380790dec 100644 --- a/src/plugins/unified_histogram/public/container/container.tsx +++ b/src/plugins/unified_histogram/public/container/container.tsx @@ -55,6 +55,10 @@ export type UnifiedHistogramContainerProps = { | 'resizeRef' | 'appendHitsCounter' | 'children' + | 'onBrushEnd' + | 'onFilter' + | 'withDefaultActions' + | 'disabledActions' >; /** diff --git a/src/plugins/unified_histogram/public/layout/layout.tsx b/src/plugins/unified_histogram/public/layout/layout.tsx index 4d0605c4f72b3..d0414f0682489 100644 --- a/src/plugins/unified_histogram/public/layout/layout.tsx +++ b/src/plugins/unified_histogram/public/layout/layout.tsx @@ -13,7 +13,12 @@ import { createHtmlPortalNode, InPortal, OutPortal } from 'react-reverse-portal' import { css } from '@emotion/css'; import type { Datatable, DatatableColumn } from '@kbn/expressions-plugin/common'; import type { DataView, DataViewField } from '@kbn/data-views-plugin/public'; -import type { LensEmbeddableInput, LensSuggestionsApi, Suggestion } from '@kbn/lens-plugin/public'; +import type { + EmbeddableComponentProps, + LensEmbeddableInput, + LensSuggestionsApi, + Suggestion, +} from '@kbn/lens-plugin/public'; import { AggregateQuery, Filter, Query, TimeRange } from '@kbn/es-query'; import { Chart } from '../chart'; import { Panels, PANELS_MODE } from '../panels'; @@ -156,6 +161,10 @@ export interface UnifiedHistogramLayoutProps extends PropsWithChildren * Callback to pass to the Lens embeddable to handle brush events */ onBrushEnd?: LensEmbeddableInput['onBrushEnd']; + /** + * Allows users to enable/disable default actions + */ + withDefaultActions?: EmbeddableComponentProps['withDefaultActions']; } export const UnifiedHistogramLayout = ({ @@ -192,6 +201,7 @@ export const UnifiedHistogramLayout = ({ onFilter, onBrushEnd, children, + withDefaultActions, }: UnifiedHistogramLayoutProps) => { const { allSuggestions, currentSuggestion, suggestionUnsupported } = useLensSuggestions({ dataView, @@ -277,6 +287,7 @@ export const UnifiedHistogramLayout = ({ onFilter={onFilter} onBrushEnd={onBrushEnd} lensTablesAdapter={lensTablesAdapter} + withDefaultActions={withDefaultActions} /> {children} diff --git a/src/plugins/unified_search/public/actions/apply_filter_action.ts b/src/plugins/unified_search/public/actions/apply_filter_action.ts index 1797aa4a2fe85..0ccf1de9f41ae 100644 --- a/src/plugins/unified_search/public/actions/apply_filter_action.ts +++ b/src/plugins/unified_search/public/actions/apply_filter_action.ts @@ -38,11 +38,13 @@ async function isCompatible(context: ApplyGlobalFilterActionContext) { export function createFilterAction( filterManager: FilterManager, timeFilter: TimefilterContract, - theme: ThemeServiceSetup + theme: ThemeServiceSetup, + id: string = ACTION_GLOBAL_APPLY_FILTER, + type: string = ACTION_GLOBAL_APPLY_FILTER ): UiActionsActionDefinition { return { - type: ACTION_GLOBAL_APPLY_FILTER, - id: ACTION_GLOBAL_APPLY_FILTER, + type, + id, order: 100, getIconType: () => 'filter', getDisplayName: () => { diff --git a/src/plugins/unified_search/public/index.ts b/src/plugins/unified_search/public/index.ts index b658a21d10cfd..fb689eb3e2d3b 100755 --- a/src/plugins/unified_search/public/index.ts +++ b/src/plugins/unified_search/public/index.ts @@ -30,6 +30,8 @@ export { ACTION_GLOBAL_APPLY_FILTER, UPDATE_FILTER_REFERENCES_ACTION } from './a export { UPDATE_FILTER_REFERENCES_TRIGGER } from './triggers'; export { createSearchBar } from './search_bar/create_search_bar'; +export { createFilterAction } from './actions/apply_filter_action'; + /* * Autocomplete query suggestions: */ diff --git a/src/plugins/visualizations/kibana.jsonc b/src/plugins/visualizations/kibana.jsonc index 22c6bd9dd32b2..69caa82b50030 100644 --- a/src/plugins/visualizations/kibana.jsonc +++ b/src/plugins/visualizations/kibana.jsonc @@ -34,7 +34,8 @@ "share", "spaces", "savedObjectsTaggingOss", - "serverless" + "serverless", + "noDataPage" ], "requiredBundles": [ "kibanaUtils", diff --git a/src/plugins/visualizations/public/plugin.ts b/src/plugins/visualizations/public/plugin.ts index 3ca1672159f24..d2805b43ed468 100644 --- a/src/plugins/visualizations/public/plugin.ts +++ b/src/plugins/visualizations/public/plugin.ts @@ -64,6 +64,7 @@ import { ContentManagementPublicSetup, ContentManagementPublicStart, } from '@kbn/content-management-plugin/public'; +import type { NoDataPagePluginStart } from '@kbn/no-data-page-plugin/public'; import type { TypesSetup, TypesStart } from './vis_types'; import type { VisualizeServices } from './visualize_app/types'; import { @@ -166,6 +167,7 @@ export interface VisualizationsStartDeps { savedObjectsManagement: SavedObjectsManagementPluginStart; contentManagement: ContentManagementPublicStart; serverless?: ServerlessPluginStart; + noDataPage?: NoDataPagePluginStart; } /** @@ -330,6 +332,7 @@ export class VisualizationsPlugin listingViewRegistry, unifiedSearch: pluginsStart.unifiedSearch, serverless: pluginsStart.serverless, + noDataPage: pluginsStart.noDataPage, }; params.element.classList.add('visAppWrapper'); diff --git a/src/plugins/visualizations/public/visualize_app/app.tsx b/src/plugins/visualizations/public/visualize_app/app.tsx index 70dd288fccaec..c7c73893eb438 100644 --- a/src/plugins/visualizations/public/visualize_app/app.tsx +++ b/src/plugins/visualizations/public/visualize_app/app.tsx @@ -14,6 +14,7 @@ import { EuiLoadingSpinner } from '@elastic/eui'; import { AppMountParameters, CoreStart } from '@kbn/core/public'; import type { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public'; import { syncGlobalQueryStateWithUrl } from '@kbn/data-plugin/public'; +import type { NoDataPagePluginStart } from '@kbn/no-data-page-plugin/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { AnalyticsNoDataPageKibanaProvider, @@ -38,6 +39,7 @@ interface NoDataComponentProps { dataViews: DataViewsContract; dataViewEditor: DataViewEditorStart; onDataViewCreated: (dataView: unknown) => void; + noDataPage?: NoDataPagePluginStart; } const NoDataComponent = ({ @@ -45,11 +47,13 @@ const NoDataComponent = ({ dataViews, dataViewEditor, onDataViewCreated, + noDataPage, }: NoDataComponentProps) => { const analyticsServices = { coreStart: core, dataViews, dataViewEditor, + noDataPage, }; return ( @@ -65,6 +69,7 @@ export const VisualizeApp = ({ onAppLeave }: VisualizeAppProps) => { core, kbnUrlStateStorage, dataViewEditor, + noDataPage, }, } = useKibana(); const { pathname } = useLocation(); @@ -125,6 +130,7 @@ export const VisualizeApp = ({ onAppLeave }: VisualizeAppProps) => { dataViewEditor={dataViewEditor} dataViews={dataViews} onDataViewCreated={onDataViewCreated} + noDataPage={noDataPage} /> ); } diff --git a/src/plugins/visualizations/public/visualize_app/types.ts b/src/plugins/visualizations/public/visualize_app/types.ts index 90806f138f9b6..77f743aaeef77 100644 --- a/src/plugins/visualizations/public/visualize_app/types.ts +++ b/src/plugins/visualizations/public/visualize_app/types.ts @@ -41,6 +41,7 @@ import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; import type { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; import type { SavedSearch, SavedSearchPublicPluginStart } from '@kbn/saved-search-plugin/public'; import type { ServerlessPluginStart } from '@kbn/serverless/public'; +import type { NoDataPagePluginStart } from '@kbn/no-data-page-plugin/public'; import type { Vis, VisualizeEmbeddableContract, @@ -117,6 +118,7 @@ export interface VisualizeServices extends CoreStart { listingViewRegistry: ListingViewRegistry; unifiedSearch: UnifiedSearchPublicPluginStart; serverless?: ServerlessPluginStart; + noDataPage?: NoDataPagePluginStart; } export interface VisInstance { diff --git a/src/plugins/visualizations/tsconfig.json b/src/plugins/visualizations/tsconfig.json index c72d4fd24d7ea..e643d9fa1fd61 100644 --- a/src/plugins/visualizations/tsconfig.json +++ b/src/plugins/visualizations/tsconfig.json @@ -62,7 +62,8 @@ "@kbn/content-management-tabbed-table-list-view", "@kbn/content-management-table-list-view", "@kbn/content-management-utils", - "@kbn/serverless" + "@kbn/serverless", + "@kbn/no-data-page-plugin" ], "exclude": [ "target/**/*", diff --git a/test/plugin_functional/test_suites/core_plugins/rendering.ts b/test/plugin_functional/test_suites/core_plugins/rendering.ts index 6e0c4be2faaec..03928a378f6f3 100644 --- a/test/plugin_functional/test_suites/core_plugins/rendering.ts +++ b/test/plugin_functional/test_suites/core_plugins/rendering.ts @@ -145,6 +145,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) { 'newsfeed.mainInterval (duration)', 'newsfeed.service.pathTemplate (string)', 'newsfeed.service.urlRoot (string)', + 'no_data_page.analyticsNoDataPageFlavor (any)', // It's a string (any because schema.conditional) 'telemetry.allowChangingOptInStatus (boolean)', 'telemetry.appendServerlessChannelsSuffix (any)', // It's a boolean (any because schema.conditional) 'telemetry.banner (boolean)', diff --git a/tsconfig.base.json b/tsconfig.base.json index cf7d73b7a5a9c..a347a249b68ca 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -674,6 +674,8 @@ "@kbn/ecs-data-quality-dashboard-plugin/*": ["x-pack/plugins/ecs_data_quality_dashboard/*"], "@kbn/elastic-assistant": ["x-pack/packages/kbn-elastic-assistant"], "@kbn/elastic-assistant/*": ["x-pack/packages/kbn-elastic-assistant/*"], + "@kbn/elastic-assistant-plugin": ["x-pack/plugins/elastic_assistant"], + "@kbn/elastic-assistant-plugin/*": ["x-pack/plugins/elastic_assistant/*"], "@kbn/elasticsearch-client-plugin": ["test/plugin_functional/plugins/elasticsearch_client_plugin"], "@kbn/elasticsearch-client-plugin/*": ["test/plugin_functional/plugins/elasticsearch_client_plugin/*"], "@kbn/elasticsearch-client-xpack-plugin": ["x-pack/test/plugin_api_integration/plugins/elasticsearch_client"], @@ -1028,6 +1030,8 @@ "@kbn/newsfeed-plugin/*": ["src/plugins/newsfeed/*"], "@kbn/newsfeed-test-plugin": ["test/common/plugins/newsfeed"], "@kbn/newsfeed-test-plugin/*": ["test/common/plugins/newsfeed/*"], + "@kbn/no-data-page-plugin": ["src/plugins/no_data_page"], + "@kbn/no-data-page-plugin/*": ["src/plugins/no_data_page/*"], "@kbn/notifications-plugin": ["x-pack/plugins/notifications"], "@kbn/notifications-plugin/*": ["x-pack/plugins/notifications/*"], "@kbn/object-versioning": ["packages/kbn-object-versioning"], @@ -1643,4 +1647,5 @@ "@kbn/ambient-storybook-types" ] } -} \ No newline at end of file +} + diff --git a/x-pack/packages/kbn-alerting-state-types/index.ts b/x-pack/packages/kbn-alerting-state-types/index.ts index c30ea9fb35f2a..80a5c3506eab8 100644 --- a/x-pack/packages/kbn-alerting-state-types/index.ts +++ b/x-pack/packages/kbn-alerting-state-types/index.ts @@ -5,20 +5,22 @@ * 2.0. */ -export type { - ThrottledActions, - LastScheduledActions, - AlertInstanceMeta, - AlertInstanceState, - AlertInstanceContext, - RawAlertInstance, -} from './src/alert_instance'; -export { rawAlertInstance } from './src/alert_instance'; - -export { DateFromString } from './src/date_from_string'; +export type { AlertInstanceContext } from './src/alert_instance'; export type { TrackedLifecycleAlertState, WrappedLifecycleRuleState } from './src/lifecycle_state'; export { wrappedStateRt } from './src/lifecycle_state'; -export type { RuleTaskState, RuleTaskParams } from './src/rule_task_instance'; -export { ActionsCompletion, ruleStateSchema, ruleParamsSchema } from './src/rule_task_instance'; +export type { RuleTaskParams } from './src/rule_task_instance'; +export { ActionsCompletion, ruleParamsSchema } from './src/rule_task_instance'; + +export type { + LatestTaskStateSchema as RuleTaskState, + MutableLatestTaskStateSchema as MutableRuleTaskState, + LatestRawAlertInstanceSchema as RawAlertInstance, + LatestAlertInstanceMetaSchema as AlertInstanceMeta, + MutableLatestAlertInstanceMetaSchema as MutableAlertInstanceMeta, + LatestAlertInstanceStateSchema as AlertInstanceState, + LatestThrottledActionSchema as ThrottledActions, + LatestLastScheduledActionsSchema as LastScheduledActions, +} from './src/task_state'; +export { stateSchemaByVersion, emptyState as emptyTaskState } from './src/task_state'; diff --git a/x-pack/packages/kbn-alerting-state-types/src/alert_instance.ts b/x-pack/packages/kbn-alerting-state-types/src/alert_instance.ts index 9685cdb656993..003fdcda742f9 100644 --- a/x-pack/packages/kbn-alerting-state-types/src/alert_instance.ts +++ b/x-pack/packages/kbn-alerting-state-types/src/alert_instance.ts @@ -6,50 +6,6 @@ */ import * as t from 'io-ts'; -import { DateFromString } from './date_from_string'; - -const actionSchema = t.type({ - date: DateFromString, -}); - -export const throttledActionSchema = t.record(t.string, actionSchema); -export type ThrottledActions = t.TypeOf; - -const lastScheduledActionsSchema = t.intersection([ - t.partial({ - subgroup: t.string, - }), - t.type({ - group: t.string, - date: DateFromString, - }), - t.partial({ actions: throttledActionSchema }), -]); - -export type LastScheduledActions = t.TypeOf; - -const metaSchema = t.partial({ - lastScheduledActions: lastScheduledActionsSchema, - // an array used to track changes in alert state, the order is based on the rule executions (oldest to most recent) - // true - alert has changed from active/recovered - // false - the status has remained either active or recovered - flappingHistory: t.array(t.boolean), - // flapping flag that indicates whether the alert is flapping - flapping: t.boolean, - maintenanceWindowIds: t.array(t.string), - pendingRecoveredCount: t.number, - uuid: t.string, -}); -export type AlertInstanceMeta = t.TypeOf; - -const stateSchema = t.record(t.string, t.unknown); -export type AlertInstanceState = t.TypeOf; const contextSchema = t.record(t.string, t.unknown); export type AlertInstanceContext = t.TypeOf; - -export const rawAlertInstance = t.partial({ - state: stateSchema, - meta: metaSchema, -}); -export type RawAlertInstance = t.TypeOf; diff --git a/x-pack/packages/kbn-alerting-state-types/src/date_from_string.test.ts b/x-pack/packages/kbn-alerting-state-types/src/date_from_string.test.ts deleted file mode 100644 index eb63507630432..0000000000000 --- a/x-pack/packages/kbn-alerting-state-types/src/date_from_string.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { DateFromString } from './date_from_string'; -import { right, isLeft } from 'fp-ts/lib/Either'; - -describe('DateFromString', () => { - test('validated and parses a string into a Date', () => { - const date = new Date(1973, 10, 30); - expect(DateFromString.decode(date.toISOString())).toEqual(right(date)); - }); - - test('validated and returns a failure for an actual Date', () => { - const date = new Date(1973, 10, 30); - expect(isLeft(DateFromString.decode(date))).toEqual(true); - }); - - test('validated and returns a failure for an invalid Date string', () => { - expect(isLeft(DateFromString.decode('1234-23-45'))).toEqual(true); - }); - - test('validated and returns a failure for a null value', () => { - expect(isLeft(DateFromString.decode(null))).toEqual(true); - }); -}); diff --git a/x-pack/packages/kbn-alerting-state-types/src/date_from_string.ts b/x-pack/packages/kbn-alerting-state-types/src/date_from_string.ts deleted file mode 100644 index 1588334a44d49..0000000000000 --- a/x-pack/packages/kbn-alerting-state-types/src/date_from_string.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import * as t from 'io-ts'; -import { either } from 'fp-ts/lib/Either'; - -// represents a Date from an ISO string -export const DateFromString = new t.Type( - 'DateFromString', - // detect the type - (value): value is Date => value instanceof Date, - (valueToDecode, context) => - either.chain( - // validate this is a string - t.string.validate(valueToDecode, context), - // decode - (value) => { - const decoded = new Date(value); - return isNaN(decoded.getTime()) ? t.failure(valueToDecode, context) : t.success(decoded); - } - ), - (valueToEncode) => valueToEncode.toISOString() -); diff --git a/x-pack/packages/kbn-alerting-state-types/src/rule_task_instance.ts b/x-pack/packages/kbn-alerting-state-types/src/rule_task_instance.ts index 415b5e665cec5..a5b881f6f124c 100644 --- a/x-pack/packages/kbn-alerting-state-types/src/rule_task_instance.ts +++ b/x-pack/packages/kbn-alerting-state-types/src/rule_task_instance.ts @@ -6,27 +6,12 @@ */ import * as t from 'io-ts'; -import { throttledActionSchema, rawAlertInstance } from './alert_instance'; -import { DateFromString } from './date_from_string'; export enum ActionsCompletion { COMPLETE = 'complete', PARTIAL = 'partial', } -export const ruleStateSchema = t.partial({ - alertTypeState: t.record(t.string, t.unknown), - // tracks the active alerts - alertInstances: t.record(t.string, rawAlertInstance), - // tracks the recovered alerts for flapping purposes - alertRecoveredInstances: t.record(t.string, rawAlertInstance), - previousStartedAt: t.union([t.null, DateFromString]), - summaryActions: throttledActionSchema, -}); - -// This is serialized in the rule task document -export type RuleTaskState = t.TypeOf; - export const ruleParamsSchema = t.intersection([ t.type({ alertId: t.string, diff --git a/x-pack/packages/kbn-alerting-state-types/src/task_state/index.ts b/x-pack/packages/kbn-alerting-state-types/src/task_state/index.ts new file mode 100644 index 0000000000000..dbceac9534ae7 --- /dev/null +++ b/x-pack/packages/kbn-alerting-state-types/src/task_state/index.ts @@ -0,0 +1,40 @@ +/* + * 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 { type TypeOf } from '@kbn/config-schema'; +import * as v1 from './v1'; + +export const stateSchemaByVersion = { + 1: v1.versionDefinition, +}; + +const latest = v1; +/** + * WARNING: Do not modify the code below when doing a new version. + * Update the "latest" variable instead. + */ +const latestTaskStateSchema = latest.versionDefinition.schema; +export type LatestTaskStateSchema = TypeOf; +export type LatestRawAlertInstanceSchema = TypeOf; +export type LatestAlertInstanceMetaSchema = TypeOf; +export type LatestAlertInstanceStateSchema = TypeOf; +export type LatestThrottledActionSchema = TypeOf; +export type LatestLastScheduledActionsSchema = TypeOf; + +export const emptyState: LatestTaskStateSchema = { + alertTypeState: {}, + alertInstances: {}, + alertRecoveredInstances: {}, + previousStartedAt: null, + summaryActions: {}, +}; + +type Mutable = { + -readonly [k in keyof T]: Mutable; +}; +export type MutableLatestTaskStateSchema = Mutable; +export type MutableLatestAlertInstanceMetaSchema = Mutable; diff --git a/x-pack/packages/kbn-alerting-state-types/src/task_state/lib/index.ts b/x-pack/packages/kbn-alerting-state-types/src/task_state/lib/index.ts new file mode 100644 index 0000000000000..219705c1185b4 --- /dev/null +++ b/x-pack/packages/kbn-alerting-state-types/src/task_state/lib/index.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { isPlainObject } from 'lodash'; + +export function isJSONObject(obj: unknown): obj is Record { + return isPlainObject(obj); +} + +export function isString(value: unknown): value is string { + return typeof value === 'string'; +} + +export function isBoolean(value: unknown): value is boolean { + return typeof value === 'boolean'; +} + +export function isNumber(value: unknown): value is number { + return typeof value === 'number'; +} + +export function isStringArray(value: unknown): value is string[] { + return Array.isArray(value) && value.every((item) => typeof item === 'string'); +} + +export function isBooleanArray(value: unknown): value is boolean[] { + return Array.isArray(value) && value.every((item) => typeof item === 'boolean'); +} diff --git a/x-pack/packages/kbn-alerting-state-types/src/task_state/v1/index.ts b/x-pack/packages/kbn-alerting-state-types/src/task_state/v1/index.ts new file mode 100644 index 0000000000000..4b28d7e68d085 --- /dev/null +++ b/x-pack/packages/kbn-alerting-state-types/src/task_state/v1/index.ts @@ -0,0 +1,23 @@ +/* + * 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 { upMigration } from './migration'; +import { versionSchema } from './schema'; + +export { + versionSchema, + throttledActionSchema, + rawAlertInstanceSchema, + metaSchema, + alertStateSchema, + lastScheduledActionsSchema, +} from './schema'; + +export const versionDefinition = { + up: upMigration, + schema: versionSchema, +}; diff --git a/x-pack/packages/kbn-alerting-state-types/src/task_state/v1/migration.test.ts b/x-pack/packages/kbn-alerting-state-types/src/task_state/v1/migration.test.ts new file mode 100644 index 0000000000000..fab82accc9a30 --- /dev/null +++ b/x-pack/packages/kbn-alerting-state-types/src/task_state/v1/migration.test.ts @@ -0,0 +1,225 @@ +/* + * 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 { + migrateThrottledActions, + migrateLastScheduledActions, + migrateMeta, + migrateAlertInstances, + upMigration, +} from './migration'; + +describe('migrateThrottledActions', () => { + it('should return undefined if input is not an object', () => { + const result = migrateThrottledActions(null); + expect(result).toBeUndefined(); + }); + + it('should return the migrated throttledActions object', () => { + const input = { + key1: { date: '2023-07-31T12:00:00Z' }, + key2: { date: '2023-07-30T12:00:00Z' }, + key3: 'notAnObject', + }; + + const expectedOutput = { + key1: { date: '2023-07-31T12:00:00Z' }, + key2: { date: '2023-07-30T12:00:00Z' }, + }; + + const result = migrateThrottledActions(input); + expect(result).toEqual(expectedOutput); + }); +}); + +describe('migrateLastScheduledActions', () => { + it('should return undefined if input is not a valid lastScheduledActions object', () => { + const result = migrateLastScheduledActions({ group: 'group1' }); // Missing 'date' property + expect(result).toBeUndefined(); + }); + + it('should return the migrated lastScheduledActions object', () => { + const input = { + group: 'group1', + subgroup: 'subgroup1', + date: '2023-07-31T12:00:00Z', + actions: { + key1: { date: '2023-07-31T12:00:00Z' }, + key2: { date: '2023-07-30T12:00:00Z' }, + }, + }; + + const expectedOutput = { + group: 'group1', + subgroup: 'subgroup1', + date: '2023-07-31T12:00:00Z', + actions: { + key1: { date: '2023-07-31T12:00:00Z' }, + key2: { date: '2023-07-30T12:00:00Z' }, + }, + }; + + const result = migrateLastScheduledActions(input); + expect(result).toEqual(expectedOutput); + }); +}); + +describe('migrateMeta', () => { + it('should return undefined if input is not an object', () => { + const result = migrateMeta(null); + expect(result).toBeUndefined(); + }); + + it('should return the migrated meta object', () => { + const input = { + lastScheduledActions: { + group: 'group1', + date: '2023-07-31T12:00:00Z', + }, + flappingHistory: [true, false, true], + flapping: true, + maintenanceWindowIds: ['id1', 'id2'], + pendingRecoveredCount: 3, + uuid: 'abc123', + }; + + const expectedOutput = { + lastScheduledActions: { + group: 'group1', + date: '2023-07-31T12:00:00Z', + }, + flappingHistory: [true, false, true], + flapping: true, + maintenanceWindowIds: ['id1', 'id2'], + pendingRecoveredCount: 3, + uuid: 'abc123', + }; + + const result = migrateMeta(input); + expect(result).toEqual(expectedOutput); + }); +}); + +describe('migrateAlertInstances', () => { + it('should return undefined if input is not an object', () => { + const result = migrateAlertInstances(null); + expect(result).toBeUndefined(); + }); + + it('should return the migrated alertInstances object', () => { + const input = { + instance1: { + meta: { + lastScheduledActions: { + group: 'group1', + date: '2023-07-31T12:00:00Z', + }, + flappingHistory: [true, false, true], + flapping: true, + maintenanceWindowIds: ['id1', 'id2'], + pendingRecoveredCount: 3, + uuid: 'abc123', + }, + state: { key: 'value' }, + }, + instance2: { + meta: { + lastScheduledActions: { + group: 'group2', + date: '2023-07-30T12:00:00Z', + }, + }, + }, + instance3: 'notAnObject', + }; + + const expectedOutput = { + instance1: { + meta: { + lastScheduledActions: { + group: 'group1', + date: '2023-07-31T12:00:00Z', + }, + flappingHistory: [true, false, true], + flapping: true, + maintenanceWindowIds: ['id1', 'id2'], + pendingRecoveredCount: 3, + uuid: 'abc123', + }, + state: { key: 'value' }, + }, + instance2: { + meta: { + lastScheduledActions: { + group: 'group2', + date: '2023-07-30T12:00:00Z', + }, + }, + }, + }; + + const result = migrateAlertInstances(input); + expect(result).toEqual(expectedOutput); + }); +}); + +describe('upMigration', () => { + it('should return the migrated state object', () => { + const inputState = { + alertTypeState: {}, + alertInstances: { + instance1: { + meta: { + lastScheduledActions: { + group: 'group1', + date: '2023-07-31T12:00:00Z', + }, + flappingHistory: [true, false, true], + flapping: true, + maintenanceWindowIds: ['id1', 'id2'], + pendingRecoveredCount: 3, + uuid: 'abc123', + }, + state: { key: 'value' }, + }, + }, + alertRecoveredInstances: {}, + previousStartedAt: '2023-07-30T12:00:00Z', + summaryActions: { + action1: { date: '2023-07-31T12:00:00Z' }, + }, + }; + + const expectedOutput = { + alertTypeState: {}, + alertInstances: { + instance1: { + meta: { + lastScheduledActions: { + group: 'group1', + date: '2023-07-31T12:00:00Z', + }, + flappingHistory: [true, false, true], + flapping: true, + maintenanceWindowIds: ['id1', 'id2'], + pendingRecoveredCount: 3, + uuid: 'abc123', + }, + state: { key: 'value' }, + }, + }, + alertRecoveredInstances: {}, + previousStartedAt: '2023-07-30T12:00:00Z', + summaryActions: { + action1: { date: '2023-07-31T12:00:00Z' }, + }, + }; + + const result = upMigration(inputState); + expect(result).toEqual(expectedOutput); + }); +}); diff --git a/x-pack/packages/kbn-alerting-state-types/src/task_state/v1/migration.ts b/x-pack/packages/kbn-alerting-state-types/src/task_state/v1/migration.ts new file mode 100644 index 0000000000000..d4e4947bcb0b8 --- /dev/null +++ b/x-pack/packages/kbn-alerting-state-types/src/task_state/v1/migration.ts @@ -0,0 +1,102 @@ +/* + * 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 { type TypeOf } from '@kbn/config-schema'; +import { isJSONObject, isString, isBoolean, isNumber, isStringArray, isBooleanArray } from '../lib'; +import { + versionSchema, + throttledActionSchema, + rawAlertInstanceSchema, + metaSchema, + lastScheduledActionsSchema, +} from './schema'; + +type VersionSchema = TypeOf; +type ThrottledActionsSchema = TypeOf; +type LastScheduledActionsSchema = TypeOf; +type RawAlertInstanceSchema = TypeOf; + +export function migrateThrottledActions( + throttledActions: unknown +): ThrottledActionsSchema | undefined { + if (!isJSONObject(throttledActions)) { + return; + } + return Object.keys(throttledActions).reduce((acc, key) => { + const val = throttledActions[key]; + if (isJSONObject(val) && isString(val.date)) { + acc[key] = { + date: val.date, + }; + } + return acc; + }, {} as TypeOf); +} + +export function migrateLastScheduledActions( + lastScheduledActions: unknown +): LastScheduledActionsSchema | undefined { + if ( + !isJSONObject(lastScheduledActions) || + !isString(lastScheduledActions.group) || + !isString(lastScheduledActions.date) + ) { + return; + } + return { + subgroup: isString(lastScheduledActions.subgroup) ? lastScheduledActions.subgroup : undefined, + group: lastScheduledActions.group, + date: lastScheduledActions.date, + actions: migrateThrottledActions(lastScheduledActions.actions), + }; +} + +export function migrateMeta(meta: unknown): TypeOf | undefined { + if (!isJSONObject(meta)) { + return; + } + return { + lastScheduledActions: migrateLastScheduledActions(meta.lastScheduledActions), + flappingHistory: isBooleanArray(meta.flappingHistory) ? meta.flappingHistory : undefined, + flapping: isBoolean(meta.flapping) ? meta.flapping : undefined, + maintenanceWindowIds: isStringArray(meta.maintenanceWindowIds) + ? meta.maintenanceWindowIds + : undefined, + pendingRecoveredCount: isNumber(meta.pendingRecoveredCount) + ? meta.pendingRecoveredCount + : undefined, + uuid: isString(meta.uuid) ? meta.uuid : undefined, + }; +} + +export function migrateAlertInstances( + alertInstances: unknown +): Record | undefined { + if (!isJSONObject(alertInstances)) { + return; + } + return Object.keys(alertInstances).reduce((acc, key) => { + const val = alertInstances[key]; + if (isJSONObject(val)) { + acc[key] = { + meta: migrateMeta(val.meta), + state: isJSONObject(val.state) ? val.state : undefined, + }; + } + return acc; + }, {} as Record); +} + +export const upMigration = (state: Record): VersionSchema => { + return { + alertTypeState: isJSONObject(state.alertTypeState) ? state.alertTypeState : undefined, + alertInstances: migrateAlertInstances(state.alertInstances), + alertRecoveredInstances: migrateAlertInstances(state.alertRecoveredInstances), + previousStartedAt: isString(state.previousStartedAt) ? state.previousStartedAt : undefined, + summaryActions: migrateThrottledActions(state.summaryActions), + }; +}; diff --git a/x-pack/packages/kbn-alerting-state-types/src/task_state/v1/schema.ts b/x-pack/packages/kbn-alerting-state-types/src/task_state/v1/schema.ts new file mode 100644 index 0000000000000..62e802483dcf7 --- /dev/null +++ b/x-pack/packages/kbn-alerting-state-types/src/task_state/v1/schema.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema } from '@kbn/config-schema'; + +const actionSchema = schema.object({ date: schema.string() }); +export const throttledActionSchema = schema.recordOf(schema.string(), actionSchema); +// TODO: Add schema by rule type for alert state +// https://github.com/elastic/kibana/issues/159344 +export const alertStateSchema = schema.recordOf(schema.string(), schema.maybe(schema.any())); +// TODO: Add schema by rule type for rule state +// https://github.com/elastic/kibana/issues/159344 +const ruleStateSchema = schema.recordOf(schema.string(), schema.maybe(schema.any())); + +export const lastScheduledActionsSchema = schema.object({ + subgroup: schema.maybe(schema.string()), + group: schema.string(), + date: schema.string(), + actions: schema.maybe(throttledActionSchema), +}); + +export const metaSchema = schema.object({ + lastScheduledActions: schema.maybe(lastScheduledActionsSchema), + // an array used to track changes in alert state, the order is based on the rule executions (oldest to most recent) + // true - alert has changed from active/recovered + // false - the status has remained either active or recovered + flappingHistory: schema.maybe(schema.arrayOf(schema.boolean())), + // flapping flag that indicates whether the alert is flapping + flapping: schema.maybe(schema.boolean()), + maintenanceWindowIds: schema.maybe(schema.arrayOf(schema.string())), + pendingRecoveredCount: schema.maybe(schema.number()), + uuid: schema.maybe(schema.string()), +}); + +export const rawAlertInstanceSchema = schema.object({ + meta: schema.maybe(metaSchema), + state: schema.maybe(alertStateSchema), +}); + +export const versionSchema = schema.object({ + alertTypeState: schema.maybe(ruleStateSchema), + // tracks the active alerts + alertInstances: schema.maybe(schema.recordOf(schema.string(), rawAlertInstanceSchema)), + // tracks the recovered alerts for flapping purposes + alertRecoveredInstances: schema.maybe(schema.recordOf(schema.string(), rawAlertInstanceSchema)), + previousStartedAt: schema.maybe(schema.nullable(schema.string())), + summaryActions: schema.maybe(throttledActionSchema), +}); diff --git a/x-pack/packages/kbn-alerting-state-types/tsconfig.json b/x-pack/packages/kbn-alerting-state-types/tsconfig.json index 6d27b06d5f8ba..e1a705d2a1ed8 100644 --- a/x-pack/packages/kbn-alerting-state-types/tsconfig.json +++ b/x-pack/packages/kbn-alerting-state-types/tsconfig.json @@ -13,5 +13,5 @@ "exclude": [ "target/**/*" ], - "kbn_references": [] + "kbn_references": ["@kbn/config-schema"] } diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.test.tsx new file mode 100644 index 0000000000000..65b8183b60a0b --- /dev/null +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.test.tsx @@ -0,0 +1,129 @@ +/* + * 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 { HttpSetup } from '@kbn/core-http-browser'; +import { OpenAiProviderType } from '@kbn/stack-connectors-plugin/public/common'; + +import { fetchConnectorExecuteAction, FetchConnectorExecuteAction } from './api'; +import type { Conversation, Message } from '../assistant_context/types'; +import { API_ERROR } from './translations'; + +jest.mock('@kbn/core-http-browser'); + +const mockHttp = { + fetch: jest.fn(), +} as unknown as HttpSetup; + +const apiConfig: Conversation['apiConfig'] = { + connectorId: 'foo', + model: 'gpt-4', + provider: OpenAiProviderType.OpenAi, +}; + +const messages: Message[] = [ + { content: 'This is a test', role: 'user', timestamp: new Date().toLocaleString() }, +]; + +describe('fetchConnectorExecuteAction', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('calls the internal assistant API when assistantLangChain is true', async () => { + const testProps: FetchConnectorExecuteAction = { + assistantLangChain: true, + http: mockHttp, + messages, + apiConfig, + }; + + await fetchConnectorExecuteAction(testProps); + + expect(mockHttp.fetch).toHaveBeenCalledWith( + '/internal/elastic_assistant/actions/connector/foo/_execute', + { + body: '{"params":{"subActionParams":{"body":"{\\"model\\":\\"gpt-4\\",\\"messages\\":[{\\"role\\":\\"user\\",\\"content\\":\\"This is a test\\"}],\\"n\\":1,\\"stop\\":null,\\"temperature\\":0.2}"},"subAction":"test"}}', + headers: { 'Content-Type': 'application/json' }, + method: 'POST', + signal: undefined, + } + ); + }); + + it('calls the actions connector api when assistantLangChain is false', async () => { + const testProps: FetchConnectorExecuteAction = { + assistantLangChain: false, + http: mockHttp, + messages, + apiConfig, + }; + + await fetchConnectorExecuteAction(testProps); + + expect(mockHttp.fetch).toHaveBeenCalledWith('/api/actions/connector/foo/_execute', { + body: '{"params":{"subActionParams":{"body":"{\\"model\\":\\"gpt-4\\",\\"messages\\":[{\\"role\\":\\"user\\",\\"content\\":\\"This is a test\\"}],\\"n\\":1,\\"stop\\":null,\\"temperature\\":0.2}"},"subAction":"test"}}', + headers: { 'Content-Type': 'application/json' }, + method: 'POST', + signal: undefined, + }); + }); + + it('returns API_ERROR when the response status is not ok', async () => { + (mockHttp.fetch as jest.Mock).mockResolvedValue({ status: 'error' }); + + const testProps: FetchConnectorExecuteAction = { + assistantLangChain: false, + http: mockHttp, + messages, + apiConfig, + }; + + const result = await fetchConnectorExecuteAction(testProps); + + expect(result).toBe(API_ERROR); + }); + + it('returns API_ERROR when there are no choices', async () => { + (mockHttp.fetch as jest.Mock).mockResolvedValue({ status: 'ok', data: {} }); + const testProps: FetchConnectorExecuteAction = { + assistantLangChain: false, + http: mockHttp, + messages, + apiConfig, + }; + + const result = await fetchConnectorExecuteAction(testProps); + + expect(result).toBe(API_ERROR); + }); + + it('return the trimmed first `choices` `message` `content` when the API call is successful', async () => { + (mockHttp.fetch as jest.Mock).mockResolvedValue({ + status: 'ok', + data: { + choices: [ + { + message: { + content: ' Test response ', // leading and trailing whitespace + }, + }, + ], + }, + }); + + const testProps: FetchConnectorExecuteAction = { + assistantLangChain: false, + http: mockHttp, + messages, + apiConfig, + }; + + const result = await fetchConnectorExecuteAction(testProps); + + expect(result).toBe('Test response'); + }); +}); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.tsx index 0bb68409caf91..c8624e365419d 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.tsx @@ -14,6 +14,7 @@ import { API_ERROR } from './translations'; import { MODEL_GPT_3_5_TURBO } from '../connectorland/models/model_selector/model_selector'; export interface FetchConnectorExecuteAction { + assistantLangChain: boolean; apiConfig: Conversation['apiConfig']; http: HttpSetup; messages: Message[]; @@ -21,6 +22,7 @@ export interface FetchConnectorExecuteAction { } export const fetchConnectorExecuteAction = async ({ + assistantLangChain, http, messages, apiConfig, @@ -54,19 +56,20 @@ export const fetchConnectorExecuteAction = async ({ }; try { + const path = assistantLangChain + ? `/internal/elastic_assistant/actions/connector/${apiConfig?.connectorId}/_execute` + : `/api/actions/connector/${apiConfig?.connectorId}/_execute`; + // TODO: Find return type for this API // eslint-disable-next-line @typescript-eslint/no-explicit-any - const response = await http.fetch( - `/api/actions/connector/${apiConfig?.connectorId}/_execute`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(requestBody), - signal, - } - ); + const response = await http.fetch(path, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(requestBody), + signal, + }); const data = response.data; if (response.status !== 'ok') { diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_title/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_title/index.tsx index 766811d70ebe6..ccf04c38f5c93 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_title/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_title/index.tsx @@ -56,7 +56,7 @@ export const AssistantTitle: React.FC<{ const content = useMemo( () => ( -

    {i18n.TOOLTIP_TITLE}

    {content}

    @@ -112,7 +111,6 @@ export const AssistantTitle: React.FC<{ {}} - onConnectorSelectionChange={() => {}} selectedConnectorId={selectedConnectorId} selectedConversation={selectedConversation} /> diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings.tsx index 853501da409c1..a0c8226b3ea7e 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings.tsx @@ -23,6 +23,8 @@ import { ModelSelector } from '../../../connectorland/models/model_selector/mode import { UseAssistantContext } from '../../../assistant_context'; import { ConversationSelectorSettings } from '../conversation_selector_settings'; import { getDefaultSystemPrompt } from '../../use_conversation/helpers'; +import { useLoadConnectors } from '../../../connectorland/use_load_connectors'; +import { getGenAiConfig } from '../../../connectorland/helpers'; export interface ConversationSettingsProps { actionTypeRegistry: ActionTypeRegistryContract; @@ -63,6 +65,8 @@ export const ConversationSettings: React.FC = React.m return getDefaultSystemPrompt({ allSystemPrompts, conversation: selectedConversation }); }, [allSystemPrompts, selectedConversation]); + const { data: connectors, isSuccess: areConnectorsFetched } = useLoadConnectors({ http }); + // Conversation callbacks // When top level conversation selection changes const onConversationSelectionChange = useCallback( @@ -131,10 +135,13 @@ export const ConversationSettings: React.FC = React.m [selectedConversation, setUpdatedConversationSettings] ); - const selectedConnectorId = useMemo( - () => selectedConversation?.apiConfig.connectorId, - [selectedConversation?.apiConfig.connectorId] - ); + const selectedConnector = useMemo(() => { + const selectedConnectorId = selectedConversation?.apiConfig.connectorId; + if (areConnectorsFetched) { + return connectors?.find((c) => c.id === selectedConnectorId); + } + return undefined; + }, [areConnectorsFetched, connectors, selectedConversation?.apiConfig.connectorId]); const selectedProvider = useMemo( () => selectedConversation?.apiConfig.provider, @@ -142,16 +149,19 @@ export const ConversationSettings: React.FC = React.m ); const handleOnConnectorSelectionChange = useCallback( - (connectorId: string, provider: OpenAiProviderType) => { + (connector) => { if (selectedConversation != null) { + const config = getGenAiConfig(connector); + setUpdatedConversationSettings((prev) => ({ ...prev, [selectedConversation.id]: { ...selectedConversation, apiConfig: { ...selectedConversation.apiConfig, - connectorId, - provider, + connectorId: connector?.id, + provider: config?.apiProvider, + model: config?.defaultModel, }, }, })); @@ -160,10 +170,11 @@ export const ConversationSettings: React.FC = React.m [selectedConversation, setUpdatedConversationSettings] ); - const selectedModel = useMemo( - () => selectedConversation?.apiConfig.model, - [selectedConversation?.apiConfig.model] - ); + const selectedModel = useMemo(() => { + const connectorModel = getGenAiConfig(selectedConnector)?.defaultModel; + // Prefer conversation configuration over connector default + return selectedConversation?.apiConfig.model ?? connectorModel; + }, [selectedConnector, selectedConversation?.apiConfig.model]); const handleOnModelSelectionChange = useCallback( (model?: string) => { @@ -244,23 +255,24 @@ export const ConversationSettings: React.FC = React.m isDisabled={selectedConversation == null} onConnectorModalVisibilityChange={() => {}} onConnectorSelectionChange={handleOnConnectorSelectionChange} - selectedConnectorId={selectedConnectorId} + selectedConnectorId={selectedConnector?.id} /> - {selectedProvider === OpenAiProviderType.OpenAi && ( - - - - )} + {selectedConnector?.isPreconfigured === false && + selectedProvider === OpenAiProviderType.OpenAi && ( + + + + )} ); } diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/translations.ts b/x-pack/packages/kbn-elastic-assistant/impl/assistant/translations.ts index 580e02247e3ee..7afa89f8f4ab6 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/translations.ts +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/translations.ts @@ -41,13 +41,6 @@ export const API_ERROR = i18n.translate('xpack.elasticAssistant.assistant.apiErr 'An error occurred sending your message. If the problem persists, please test the connector configuration.', }); -export const TOOLTIP_TITLE = i18n.translate( - 'xpack.elasticAssistant.assistant.technicalPreview.tooltipTitle', - { - defaultMessage: 'Beta', - } -); - export const TOOLTIP_ARIA_LABEL = i18n.translate( 'xpack.elasticAssistant.documentationLinks.ariaLabel', { diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_send_messages/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_send_messages/index.tsx index 3a3b72c253862..c68d82d99b9ac 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_send_messages/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_send_messages/index.tsx @@ -8,6 +8,8 @@ import { useCallback, useState } from 'react'; import { HttpSetup } from '@kbn/core-http-browser'; + +import { useAssistantContext } from '../../assistant_context'; import { Conversation, Message } from '../../assistant_context/types'; import { fetchConnectorExecuteAction } from '../api'; @@ -23,20 +25,25 @@ interface UseSendMessages { } export const useSendMessages = (): UseSendMessages => { + const { assistantLangChain } = useAssistantContext(); const [isLoading, setIsLoading] = useState(false); - const sendMessages = useCallback(async ({ apiConfig, http, messages }: SendMessagesProps) => { - setIsLoading(true); - try { - return await fetchConnectorExecuteAction({ - http, - messages, - apiConfig, - }); - } finally { - setIsLoading(false); - } - }, []); + const sendMessages = useCallback( + async ({ apiConfig, http, messages }: SendMessagesProps) => { + setIsLoading(true); + try { + return await fetchConnectorExecuteAction({ + assistantLangChain, + http, + messages, + apiConfig, + }); + } finally { + setIsLoading(false); + } + }, + [assistantLangChain] + ); return { isLoading, sendMessages }; }; diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/index.test.tsx index 466ccbb83cb0f..6e39da055043a 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/index.test.tsx @@ -28,6 +28,7 @@ const ContextWrapper: React.FC = ({ children }) => ( CodeBlockDetails[][]; baseAllow: string[]; @@ -85,6 +86,7 @@ export interface UseAssistantContext { augmentMessageCodeBlocks: (currentConversation: Conversation) => CodeBlockDetails[][]; allQuickPrompts: QuickPrompt[]; allSystemPrompts: Prompt[]; + assistantLangChain: boolean; baseAllow: string[]; baseAllowReplacement: string[]; docLinks: Omit; @@ -129,6 +131,7 @@ const AssistantContext = React.createContext(un export const AssistantProvider: React.FC = ({ actionTypeRegistry, assistantAvailability, + assistantLangChain, assistantTelemetry, augmentMessageCodeBlocks, baseAllow, @@ -248,6 +251,7 @@ export const AssistantProvider: React.FC = ({ () => ({ actionTypeRegistry, assistantAvailability, + assistantLangChain, assistantTelemetry, augmentMessageCodeBlocks, allQuickPrompts: localStorageQuickPrompts ?? [], @@ -284,6 +288,7 @@ export const AssistantProvider: React.FC = ({ [ actionTypeRegistry, assistantAvailability, + assistantLangChain, assistantTelemetry, augmentMessageCodeBlocks, baseAllow, diff --git a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector/index.tsx index f1a03329657b6..df3f0b54cd14f 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector/index.tsx @@ -14,31 +14,24 @@ import { } from '@kbn/triggers-actions-ui-plugin/public'; import { HttpSetup } from '@kbn/core-http-browser'; -import { ActionConnectorProps } from '@kbn/triggers-actions-ui-plugin/public/types'; import { ConnectorAddModal } from '@kbn/triggers-actions-ui-plugin/public/common/constants'; -import { - GEN_AI_CONNECTOR_ID, - OpenAiProviderType, -} from '@kbn/stack-connectors-plugin/public/common'; +import { GEN_AI_CONNECTOR_ID } from '@kbn/stack-connectors-plugin/public/common'; import { useLoadConnectors } from '../use_load_connectors'; import * as i18n from '../translations'; import { useLoadActionTypes } from '../use_load_action_types'; import { useAssistantContext } from '../../assistant_context'; +import { getGenAiConfig } from '../helpers'; export const ADD_NEW_CONNECTOR = 'ADD_NEW_CONNECTOR'; interface Props { actionTypeRegistry: ActionTypeRegistryContract; http: HttpSetup; isDisabled?: boolean; - onConnectorSelectionChange: (connectorId: string, provider: OpenAiProviderType) => void; + onConnectorSelectionChange: (connector: ActionConnector | undefined) => void; selectedConnectorId?: string; onConnectorModalVisibilityChange?: (isVisible: boolean) => void; } -interface Config { - apiProvider: string; -} - export const ConnectorSelector: React.FC = React.memo( ({ actionTypeRegistry, @@ -95,18 +88,19 @@ export const ConnectorSelector: React.FC = React.memo( const connectorOptions = useMemo(() => { return ( connectors?.map((connector) => { - const apiProvider: string | undefined = ( - connector as ActionConnectorProps - )?.config?.apiProvider; + const apiProvider = getGenAiConfig(connector)?.apiProvider; + const connectorDetails = connector.isPreconfigured + ? i18n.PRECONFIGURED_CONNECTOR + : apiProvider; return { value: connector.id, inputDisplay: connector.name, dropdownDisplay: ( {connector.name} - {apiProvider && ( - -

    {apiProvider}

    + {connectorDetails && ( + +

    {connectorDetails}

    )}
    @@ -138,10 +132,8 @@ export const ConnectorSelector: React.FC = React.memo( return; } - const apiProvider = ( - connectors?.find((c) => c.id === connectorId) as ActionConnectorProps - )?.config.apiProvider as OpenAiProviderType; - onConnectorSelectionChange(connectorId, apiProvider); + const connector = connectors?.find((c) => c.id === connectorId); + onConnectorSelectionChange(connector); }, [connectors, onConnectorSelectionChange, onConnectorModalVisibilityChange] ); @@ -162,12 +154,8 @@ export const ConnectorSelector: React.FC = React.memo( { - onConnectorSelectionChange( - savedAction.id, - (savedAction as ActionConnectorProps)?.config - .apiProvider as OpenAiProviderType - ); + postSaveEventHandler={(connector: ActionConnector) => { + onConnectorSelectionChange(connector); refetchConnectors?.(); cleanupAndCloseModal(); }} diff --git a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector_inline/connector_selector_inline.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector_inline/connector_selector_inline.test.tsx index 774098eba8b2e..3b1ff0be86181 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector_inline/connector_selector_inline.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector_inline/connector_selector_inline.test.tsx @@ -65,7 +65,6 @@ describe('ConnectorSelectorInline', () => { @@ -85,7 +84,6 @@ describe('ConnectorSelectorInline', () => { @@ -105,7 +103,6 @@ describe('ConnectorSelectorInline', () => { diff --git a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector_inline/connector_selector_inline.tsx b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector_inline/connector_selector_inline.tsx index 66d7971731deb..d7f4a50f90ae9 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector_inline/connector_selector_inline.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector_inline/connector_selector_inline.tsx @@ -10,7 +10,6 @@ import React, { useCallback, useMemo, useState } from 'react'; import { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public'; -import { ActionConnectorProps } from '@kbn/triggers-actions-ui-plugin/public/types'; import { ConnectorAddModal } from '@kbn/triggers-actions-ui-plugin/public/common/constants'; import { GEN_AI_CONNECTOR_ID, @@ -23,20 +22,16 @@ import * as i18n from '../translations'; import { useLoadActionTypes } from '../use_load_action_types'; import { useAssistantContext } from '../../assistant_context'; import { useConversation } from '../../assistant/use_conversation'; +import { getGenAiConfig } from '../helpers'; export const ADD_NEW_CONNECTOR = 'ADD_NEW_CONNECTOR'; interface Props { isDisabled?: boolean; - onConnectorSelectionChange: (connectorId: string, provider: OpenAiProviderType) => void; selectedConnectorId?: string; selectedConversation?: Conversation; onConnectorModalVisibilityChange?: (isVisible: boolean) => void; } -interface Config { - apiProvider: string; -} - const inputContainerClassName = css` height: 32px; @@ -82,7 +77,6 @@ export const ConnectorSelectorInline: React.FC = React.memo( onConnectorModalVisibilityChange, selectedConnectorId, selectedConversation, - onConnectorSelectionChange, }) => { const [isOpen, setIsOpen] = useState(false); const { actionTypeRegistry, assistantAvailability, http } = useAssistantContext(); @@ -136,9 +130,10 @@ export const ConnectorSelectorInline: React.FC = React.memo( const connectorOptions = useMemo(() => { return ( connectors?.map((connector) => { - const apiProvider: string | undefined = ( - connector as ActionConnectorProps - )?.config?.apiProvider; + const apiProvider = getGenAiConfig(connector)?.apiProvider; + const connectorDetails = connector.isPreconfigured + ? i18n.PRECONFIGURED_CONNECTOR + : apiProvider; return { value: connector.id, inputDisplay: ( @@ -149,9 +144,9 @@ export const ConnectorSelectorInline: React.FC = React.memo( dropdownDisplay: ( {connector.name} - {apiProvider && ( + {connectorDetails && ( -

    {apiProvider}

    +

    {connectorDetails}

    )}
    @@ -182,7 +177,7 @@ export const ConnectorSelectorInline: React.FC = React.memo( const handleOnBlur = useCallback(() => setIsOpen(false), []); const onChange = useCallback( - (connectorId: string, apiProvider?: OpenAiProviderType) => { + (connectorId: string, apiProvider?: OpenAiProviderType, model?: string) => { setIsOpen(false); if (connectorId === ADD_NEW_CONNECTOR) { @@ -191,31 +186,22 @@ export const ConnectorSelectorInline: React.FC = React.memo( return; } - const provider = - apiProvider ?? - ((connectors?.find((c) => c.id === connectorId) as ActionConnectorProps) - ?.config.apiProvider as OpenAiProviderType); - + const connector = connectors?.find((c) => c.id === connectorId); + const config = getGenAiConfig(connector); if (selectedConversation != null) { setApiConfig({ conversationId: selectedConversation.id, apiConfig: { ...selectedConversation.apiConfig, connectorId, - provider, + // With the inline component, prefer config args to handle 'new connector' case + provider: apiProvider ?? config?.apiProvider, + model: model ?? config?.defaultModel, }, }); } - - onConnectorSelectionChange(connectorId, provider); }, - [ - connectors, - selectedConversation, - onConnectorSelectionChange, - onConnectorModalVisibilityChange, - setApiConfig, - ] + [connectors, selectedConversation, onConnectorModalVisibilityChange, setApiConfig] ); const placeholderComponent = useMemo( @@ -276,11 +262,9 @@ export const ConnectorSelectorInline: React.FC = React.memo( { - const provider = (savedAction as ActionConnectorProps)?.config - .apiProvider as OpenAiProviderType; - onChange(savedAction.id, provider); - onConnectorSelectionChange(savedAction.id, provider); + postSaveEventHandler={(connector: ActionConnector) => { + const config = getGenAiConfig(connector); + onChange(connector.id, config?.apiProvider, config?.defaultModel); refetchConnectors?.(); cleanupAndCloseModal(); }} diff --git a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_setup/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_setup/index.tsx index 23ccc51943655..9429ad9435ea7 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_setup/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_setup/index.tsx @@ -14,11 +14,7 @@ import { ConnectorAddModal } from '@kbn/triggers-actions-ui-plugin/public/common import type { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public'; import { ActionType } from '@kbn/triggers-actions-ui-plugin/public'; -import { - GEN_AI_CONNECTOR_ID, - OpenAiProviderType, -} from '@kbn/stack-connectors-plugin/public/common'; -import { ActionConnectorProps } from '@kbn/triggers-actions-ui-plugin/public/types'; +import { GEN_AI_CONNECTOR_ID } from '@kbn/stack-connectors-plugin/public/common'; import { WELCOME_CONVERSATION } from '../../assistant/use_conversation/sample_conversations'; import { Conversation, Message } from '../../..'; import { useLoadActionTypes } from '../use_load_action_types'; @@ -30,6 +26,7 @@ import * as i18n from '../translations'; import { useAssistantContext } from '../../assistant_context'; import { useLoadConnectors } from '../use_load_connectors'; import { AssistantAvatar } from '../../assistant/assistant_avatar/assistant_avatar'; +import { getGenAiConfig } from '../helpers'; const ConnectorButtonWrapper = styled.div` margin-bottom: 10px; @@ -39,10 +36,6 @@ const SkipEuiText = styled(EuiText)` margin-top: 20px; `; -interface Config { - apiProvider: string; -} - export interface ConnectorSetupProps { conversation?: Conversation; onSetupComplete?: () => void; @@ -223,16 +216,17 @@ export const useConnectorSetup = ({ setIsConnectorModalVisible(false)} - postSaveEventHandler={(savedAction: ActionConnector) => { + postSaveEventHandler={(connector: ActionConnector) => { + const config = getGenAiConfig(connector); // Add connector to all conversations Object.values(conversations).forEach((c) => { setApiConfig({ conversationId: c.id, apiConfig: { ...c.apiConfig, - connectorId: savedAction.id, - provider: (savedAction as ActionConnectorProps)?.config - .apiProvider as OpenAiProviderType, + connectorId: connector.id, + provider: config?.apiProvider, + model: config?.defaultModel, }, }); }); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/helpers.tsx b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/helpers.tsx new file mode 100644 index 0000000000000..ffd9604ab328f --- /dev/null +++ b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/helpers.tsx @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public'; +import { ActionConnectorProps } from '@kbn/triggers-actions-ui-plugin/public/types'; +import { OpenAiProviderType } from '@kbn/stack-connectors-plugin/common/gen_ai/constants'; + +interface GenAiConfig { + apiProvider?: OpenAiProviderType; + apiUrl?: string; + defaultModel?: string; +} + +/** + * Returns the GenAiConfig for a given ActionConnector. Note that if the connector is preconfigured, + * the config will be undefined as the connector is neither available nor editable. + * + * TODO: Extract and use separate types from GenAiConfig from '@kbn/stack-connectors-plugin/common/gen_ai/types' + * + * @param connector + */ +export const getGenAiConfig = (connector: ActionConnector | undefined): GenAiConfig | undefined => { + if (!connector?.isPreconfigured) { + return (connector as ActionConnectorProps)?.config; + } + return undefined; +}; diff --git a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/translations.ts b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/translations.ts index dbc692d6b5493..ae84527b21c7d 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/translations.ts +++ b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/translations.ts @@ -31,6 +31,13 @@ export const WELCOME_SECURITY = i18n.translate( } ); +export const PRECONFIGURED_CONNECTOR = i18n.translate( + 'xpack.elasticAssistant.assistant.connectors.preconfiguredTitle', + { + defaultMessage: 'Preconfigured', + } +); + export const CONNECTOR_SELECTOR_TITLE = i18n.translate( 'xpack.elasticAssistant.assistant.connectors.connectorSelector.ariaLabel', { diff --git a/x-pack/packages/kbn-elastic-assistant/impl/mock/test_providers/test_providers.tsx b/x-pack/packages/kbn-elastic-assistant/impl/mock/test_providers/test_providers.tsx index aec3a6262ba4c..484dd316cc0ac 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/mock/test_providers/test_providers.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/mock/test_providers/test_providers.tsx @@ -66,6 +66,7 @@ export const TestProvidersComponent: React.FC = ({ = ({ children, isILMAvailab { if (isCompleteResponse(result)) { resolve(processCategoryResults(result, field, unwrap)); - } else if (isErrorResponse(result)) { - reject(result); } else { // partial results // Ignore partial results for now. diff --git a/x-pack/plugins/aiops/public/hooks/use_cancellable_search.ts b/x-pack/plugins/aiops/public/hooks/use_cancellable_search.ts index 0450905bde912..a44e176c1c073 100644 --- a/x-pack/plugins/aiops/public/hooks/use_cancellable_search.ts +++ b/x-pack/plugins/aiops/public/hooks/use_cancellable_search.ts @@ -6,11 +6,7 @@ */ import { useCallback, useRef, useState } from 'react'; -import { - type IKibanaSearchResponse, - isCompleteResponse, - isErrorResponse, -} from '@kbn/data-plugin/common'; +import { type IKibanaSearchResponse, isCompleteResponse } from '@kbn/data-plugin/common'; import { tap } from 'rxjs/operators'; import { useAiopsAppContext } from './use_aiops_app_context'; @@ -38,8 +34,6 @@ export function useCancellableSearch() { if (isCompleteResponse(result)) { setIsFetching(false); resolve(result); - } else if (isErrorResponse(result)) { - reject(result); } else { // partial results // Ignore partial results for now. diff --git a/x-pack/plugins/alerting/README.md b/x-pack/plugins/alerting/README.md index ba8a7ea8f763f..508a70874e37e 100644 --- a/x-pack/plugins/alerting/README.md +++ b/x-pack/plugins/alerting/README.md @@ -30,7 +30,7 @@ Table of Contents - [Internal HTTP APIs](#internal-http-apis) - [`GET /internal/alerting/rule/{id}/state`: Get rule state](#get-internalalertingruleidstate-get-rule-state) - [`GET /internal/alerting/rule/{id}/_alert_summary`: Get rule alert summary](#get-internalalertingruleidalertsummary-get-rule-alert-summary) - - [`POST /internal/alerting/rule/{id}/_update_api_key`: Update rule API key](#post-internalalertingruleidupdateapikey-update-rule-api-key) + - [`POST /api/alerting/rule/{id}/_update_api_key`: Update rule API key](#post-internalalertingruleidupdateapikey-update-rule-api-key) - [Alert Factory](#alert-factory) - [Templating Actions](#templating-actions) - [Examples](#examples) @@ -306,7 +306,7 @@ interface MyRuleTypeAlertContext extends AlertInstanceContext { } type MyRuleTypeActionGroups = 'default' | 'warning'; - + const myRuleType: RuleType< MyRuleTypeParams, MyRuleTypeExtractedParams, @@ -380,9 +380,9 @@ const myRuleType: RuleType< // Only execute if CPU usage is greater than threshold if (currentCpuUsage > threshold) { - // The first argument is a unique identifier for the alert. In this - // scenario the provided server will be used. Also, this ID will be - // used to make `getState()` return previous state, if any, on + // The first argument is a unique identifier for the alert. In this + // scenario the provided server will be used. Also, this ID will be + // used to make `getState()` return previous state, if any, on // matching identifiers. const alert = services.alertFactory.create(server); @@ -395,7 +395,7 @@ const myRuleType: RuleType< cpuUsage: currentCpuUsage, }); - // 'default' refers to the id of a group of actions to be scheduled + // 'default' refers to the id of a group of actions to be scheduled // for execution, see 'actions' in create rule section alert.scheduleActions('default', { server, @@ -406,8 +406,8 @@ const myRuleType: RuleType< // Returning updated rule type level state, this will become available // within the `state` function parameter at the next execution return { - // This is an example attribute you could set, it makes more sense - // to use this state when the rule type executes multiple + // This is an example attribute you could set, it makes more sense + // to use this state when the rule type executes multiple // alerts but wants a single place to track certain values. lastChecked: new Date(), }; @@ -497,7 +497,7 @@ features.registerKibanaFeature({ // grant `read` over our own type 'my-application-id.my-alert-type', // grant `read` over the built-in IndexThreshold - '.index-threshold', + '.index-threshold', // grant `read` over Uptime's TLS RuleType 'xpack.uptime.alerts.actionGroups.tls' ], @@ -507,7 +507,7 @@ features.registerKibanaFeature({ // grant `read` over our own type 'my-application-id.my-alert-type', // grant `read` over the built-in IndexThreshold - '.index-threshold', + '.index-threshold', // grant `read` over Uptime's TLS RuleType 'xpack.uptime.alerts.actionGroups.tls' ], @@ -555,7 +555,7 @@ features.registerKibanaFeature({ read: [ 'my-application-id.my-restricted-rule-type' ], - }, + }, alert: { all: [ 'my-application-id.my-rule-type' @@ -563,7 +563,7 @@ features.registerKibanaFeature({ read: [ 'my-application-id.my-restricted-rule-type' ], - }, + }, }, savedObject: { all: [], @@ -798,7 +798,7 @@ Query: |---|---|---| |dateStart|The date to start looking for alert events in the event log. Either an ISO date string, or a duration string indicating time since now.|string| -### `POST /internal/alerting/rule/{id}/_update_api_key`: Update rule API key +### `POST /api/alerting/rule/{id}/_update_api_key`: Update rule API key |Property|Description|Type| |---|---|---| diff --git a/x-pack/plugins/alerting/common/index.ts b/x-pack/plugins/alerting/common/index.ts index 3134ed5b69fdb..05b9e84ee2b7a 100644 --- a/x-pack/plugins/alerting/common/index.ts +++ b/x-pack/plugins/alerting/common/index.ts @@ -25,14 +25,6 @@ export type { RuleTaskState, RuleTaskParams, } from '@kbn/alerting-state-types'; -export { - rawAlertInstance, - DateFromString, - wrappedStateRt, - ActionsCompletion, - ruleStateSchema, - ruleParamsSchema, -} from '@kbn/alerting-state-types'; export * from './alert_summary'; export * from './builtin_action_groups'; export * from './bulk_edit'; diff --git a/x-pack/plugins/alerting/docs/openapi/bundled.json b/x-pack/plugins/alerting/docs/openapi/bundled.json index 31b043f812d47..7a169c842a8e6 100644 --- a/x-pack/plugins/alerting/docs/openapi/bundled.json +++ b/x-pack/plugins/alerting/docs/openapi/bundled.json @@ -12,18 +12,26 @@ "url": "https://www.elastic.co/licensing/elastic-license" } }, - "tags": [ - { - "name": "alerting", - "description": "Alerting APIs enable you to create and manage rules and alerts." - } - ], "servers": [ { "url": "http://localhost:5601", "description": "local" } ], + "security": [ + { + "basicAuth": [] + }, + { + "apiKeyAuth": [] + } + ], + "tags": [ + { + "name": "alerting", + "description": "Alerting APIs enable you to create and manage rules and alerts." + } + ], "paths": { "/s/{spaceId}/api/alerting/rule": { "post": { @@ -52,6 +60,9 @@ "createEsQueryRuleRequest": { "$ref": "#/components/examples/create_es_query_rule_request" }, + "createEsQueryKqlRuleRequest": { + "$ref": "#/components/examples/create_es_query_kql_rule_request" + }, "createIndexThresholdRuleRequest": { "$ref": "#/components/examples/create_index_threshold_rule_request" } @@ -71,6 +82,9 @@ "createEsQueryRuleResponse": { "$ref": "#/components/examples/create_es_query_rule_response" }, + "createEsQueryKqlRuleResponse": { + "$ref": "#/components/examples/create_es_query_kql_rule_response" + }, "createIndexThresholdRuleResponse": { "$ref": "#/components/examples/create_index_threshold_rule_response" } @@ -255,6 +269,9 @@ "createEsQueryRuleIdRequest": { "$ref": "#/components/examples/create_es_query_rule_request" }, + "createEsQueryKqlRuleIdRequest": { + "$ref": "#/components/examples/create_es_query_kql_rule_request" + }, "createIndexThreholdRuleIdRequest": { "$ref": "#/components/examples/create_index_threshold_rule_request" } @@ -274,6 +291,9 @@ "createEsQueryRuleIdResponse": { "$ref": "#/components/examples/create_es_query_rule_response" }, + "createEsQueryKqlRuleIdResponse": { + "$ref": "#/components/examples/create_es_query_kql_rule_response" + }, "createIndexThresholdRuleIdResponse": { "$ref": "#/components/examples/create_index_threshold_rule_response" } @@ -1188,6 +1208,52 @@ } ] }, + "/s/{spaceId}/api/alerting/rule/{ruleId}/_update_api_key": { + "post": { + "summary": "Updates the API key for a rule.", + "operationId": "updateRuleAPIKey", + "description": "The new API key has the credentials of the user that submits the request.", + "tags": [ + "alerting" + ], + "parameters": [ + { + "$ref": "#/components/parameters/kbn_xsrf" + }, + { + "$ref": "#/components/parameters/rule_id" + }, + { + "$ref": "#/components/parameters/space_id" + } + ], + "responses": { + "200": { + "description": "Indicates a successful call." + }, + "400": { + "description": "Bad request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/400_response" + } + } + } + } + }, + "servers": [ + { + "url": "https://localhost:5601" + } + ] + }, + "servers": [ + { + "url": "https://localhost:5601" + } + ] + }, "/s/{spaceId}/api/alerting/rule/{ruleId}/alert/{alertId}/_mute": { "post": { "summary": "Mutes an alert.", @@ -6595,6 +6661,32 @@ } } }, + "400_response": { + "title": "Bad request", + "type": "object", + "required": [ + "error", + "message", + "statusCode" + ], + "properties": { + "error": { + "type": "string", + "enum": [ + "Bad Request" + ] + }, + "message": { + "type": "string" + }, + "statusCode": { + "type": "integer", + "enum": [ + 400 + ] + } + } + }, "alert_response_properties": { "title": "Legacy alert response properties", "type": "object", @@ -6710,10 +6802,62 @@ }, "examples": { "create_es_query_rule_request": { - "summary": "Create an Elasticsearch query rule.", + "summary": "Create an Elasticsearch query rule that uses Elasticsearch query domain specific language (DSL) to define its query and a server log connector to send notifications.", "value": { + "actions": [ + { + "group": "query matched", + "params": { + "level": "info", + "message": "The system has detected {{alerts.new.count}} new, {{alerts.ongoing.count}} ongoing, and {{alerts.recovered.count}} recovered alerts." + }, + "id": "fdbece50-406c-11ee-850e-c71febc4ca7f", + "frequency": { + "throttle": "1d", + "summary": true, + "notify_when": "onThrottleInterval" + } + }, + { + "group": "recovered", + "params": { + "level": "info", + "message": "Recovered" + }, + "id": "fdbece50-406c-11ee-850e-c71febc4ca7f", + "frequency": { + "summary": false, + "notify_when": "onActionGroupChange" + } + } + ], "consumer": "alerts", "name": "my Elasticsearch query rule", + "params": { + "esQuery": "\"\"\"{\"query\":{\"match_all\" : {}}}\"\"\"", + "index": [ + "kibana_sample_data_logs" + ], + "size": 100, + "threshold": [ + 100 + ], + "thresholdComparator": ">", + "timeField": "@timestamp", + "timeWindowSize": 1, + "timeWindowUnit": "d" + }, + "rule_type_id": ".es-query", + "schedule": { + "interval": "1d" + } + } + }, + "create_es_query_kql_rule_request": { + "summary": "Create an Elasticsearch query rule that uses Kibana query language (KQL).", + "value": { + "consumer": "alerts", + "name": "my Elasticsearch query KQL rule", "params": { "aggType": "count", "excludeHitsFromPreviousRun": true, @@ -6786,11 +6930,92 @@ } }, "create_es_query_rule_response": { + "summary": "The create rule API returns a JSON object that contains details about the rule.", + "value": { + "id": "58148c70-407f-11ee-850e-c71febc4ca7f", + "enabled": true, + "name": "my Elasticsearch query rule", + "tags": [], + "rule_type_id": ".es-query", + "consumer": "alerts", + "schedule": { + "interval": "1d" + }, + "actions": [ + { + "group": "query matched", + "id": "fdbece50-406c-11ee-850e-c71febc4ca7f", + "params": { + "level": "info", + "message": "The system has detected {{alerts.new.count}} new, {{alerts.ongoing.count}} ongoing, and {{alerts.recovered.count}} recovered alerts." + }, + "connector_type_id": ".server-log", + "frequency": { + "summary": true, + "notify_when": "onThrottleInterval", + "throttle": "1d" + }, + "uuid": "53f3c2a3-e5d0-4cfa-af3b-6f0881385e78" + }, + { + "group": "recovered", + "id": "fdbece50-406c-11ee-850e-c71febc4ca7f", + "params": { + "level": "info", + "message": "Recovered" + }, + "connector_type_id": ".server-log", + "frequency": { + "summary": false, + "notify_when": "onActionGroupChange", + "throttle": null + }, + "uuid": "2324e45b-c0df-45c7-9d70-4993e30be758" + } + ], + "params": { + "thresholdComparator": ">", + "timeWindowSize": 1, + "timeWindowUnit": "d", + "threshold": [ + 100 + ], + "size": 100, + "timeField": "@timestamp", + "index": [ + "kibana_sample_data_logs" + ], + "esQuery": "\"\"\"{\"query\":{\"match_all\" : {}}}\"\"\"", + "excludeHitsFromPreviousRun": true, + "aggType": "count", + "groupBy": "all", + "searchType": "esQuery" + }, + "scheduled_task_id": "58148c70-407f-11ee-850e-c71febc4ca7f", + "created_by": "elastic", + "updated_by": "elastic", + "created_at": "2023-08-22T00:03:38.263Z", + "updated_at": "2023-08-22T00:03:38.263Z", + "api_key_owner": "elastic", + "api_key_created_by_user": false, + "throttle": null, + "mute_all": false, + "notify_when": null, + "muted_alert_ids": [], + "execution_status": { + "status": "pending", + "last_execution_date": "2023-08-22T00:03:38.263Z" + }, + "revision": 0, + "running": false + } + }, + "create_es_query_kql_rule_response": { "summary": "The create rule API returns a JSON object that contains details about the rule.", "value": { "id": "7bd506d0-2284-11ee-8fad-6101956ced88", "enabled": true, - "name": "my Elasticsearch query rule\"", + "name": "my Elasticsearch query KQL rule\"", "tags": [], "rule_type_id": ".es-query", "consumer": "alerts", @@ -7504,13 +7729,5 @@ ] } } - }, - "security": [ - { - "basicAuth": [] - }, - { - "apiKeyAuth": [] - } - ] + } } \ No newline at end of file diff --git a/x-pack/plugins/alerting/docs/openapi/bundled.yaml b/x-pack/plugins/alerting/docs/openapi/bundled.yaml index 72c8cf2da3828..0465a9218f2db 100644 --- a/x-pack/plugins/alerting/docs/openapi/bundled.yaml +++ b/x-pack/plugins/alerting/docs/openapi/bundled.yaml @@ -8,12 +8,15 @@ info: license: name: Elastic License 2.0 url: https://www.elastic.co/licensing/elastic-license -tags: - - name: alerting - description: Alerting APIs enable you to create and manage rules and alerts. servers: - url: http://localhost:5601 description: local +security: + - basicAuth: [] + - apiKeyAuth: [] +tags: + - name: alerting + description: Alerting APIs enable you to create and manage rules and alerts. paths: /s/{spaceId}/api/alerting/rule: post: @@ -35,6 +38,8 @@ paths: examples: createEsQueryRuleRequest: $ref: '#/components/examples/create_es_query_rule_request' + createEsQueryKqlRuleRequest: + $ref: '#/components/examples/create_es_query_kql_rule_request' createIndexThresholdRuleRequest: $ref: '#/components/examples/create_index_threshold_rule_request' responses: @@ -47,6 +52,8 @@ paths: examples: createEsQueryRuleResponse: $ref: '#/components/examples/create_es_query_rule_response' + createEsQueryKqlRuleResponse: + $ref: '#/components/examples/create_es_query_kql_rule_response' createIndexThresholdRuleResponse: $ref: '#/components/examples/create_index_threshold_rule_response' '401': @@ -155,6 +162,8 @@ paths: examples: createEsQueryRuleIdRequest: $ref: '#/components/examples/create_es_query_rule_request' + createEsQueryKqlRuleIdRequest: + $ref: '#/components/examples/create_es_query_kql_rule_request' createIndexThreholdRuleIdRequest: $ref: '#/components/examples/create_index_threshold_rule_request' responses: @@ -167,6 +176,8 @@ paths: examples: createEsQueryRuleIdResponse: $ref: '#/components/examples/create_es_query_rule_response' + createEsQueryKqlRuleIdResponse: + $ref: '#/components/examples/create_es_query_kql_rule_response' createIndexThresholdRuleIdResponse: $ref: '#/components/examples/create_index_threshold_rule_response' '401': @@ -732,6 +743,30 @@ paths: - url: https://localhost:5601 servers: - url: https://localhost:5601 + /s/{spaceId}/api/alerting/rule/{ruleId}/_update_api_key: + post: + summary: Updates the API key for a rule. + operationId: updateRuleAPIKey + description: The new API key has the credentials of the user that submits the request. + tags: + - alerting + parameters: + - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/components/parameters/rule_id' + - $ref: '#/components/parameters/space_id' + responses: + '200': + description: Indicates a successful call. + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/400_response' + servers: + - url: https://localhost:5601 + servers: + - url: https://localhost:5601 /s/{spaceId}/api/alerting/rule/{ruleId}/alert/{alertId}/_mute: post: summary: Mutes an alert. @@ -4503,6 +4538,24 @@ components: $ref: '#/components/schemas/tags' throttle: $ref: '#/components/schemas/throttle' + 400_response: + title: Bad request + type: object + required: + - error + - message + - statusCode + properties: + error: + type: string + enum: + - Bad Request + message: + type: string + statusCode: + type: integer + enum: + - 400 alert_response_properties: title: Legacy alert response properties type: object @@ -4588,10 +4641,47 @@ components: example: elastic examples: create_es_query_rule_request: - summary: Create an Elasticsearch query rule. + summary: Create an Elasticsearch query rule that uses Elasticsearch query domain specific language (DSL) to define its query and a server log connector to send notifications. value: + actions: + - group: query matched + params: + level: info + message: The system has detected {{alerts.new.count}} new, {{alerts.ongoing.count}} ongoing, and {{alerts.recovered.count}} recovered alerts. + id: fdbece50-406c-11ee-850e-c71febc4ca7f + frequency: + throttle: 1d + summary: true + notify_when: onThrottleInterval + - group: recovered + params: + level: info + message: Recovered + id: fdbece50-406c-11ee-850e-c71febc4ca7f + frequency: + summary: false + notify_when: onActionGroupChange consumer: alerts name: my Elasticsearch query rule + params: + esQuery: '"""{"query":{"match_all" : {}}}"""' + index: + - kibana_sample_data_logs + size: 100 + threshold: + - 100 + thresholdComparator: '>' + timeField: '@timestamp' + timeWindowSize: 1 + timeWindowUnit: d + rule_type_id: .es-query + schedule: + interval: 1d + create_es_query_kql_rule_request: + summary: Create an Elasticsearch query rule that uses Kibana query language (KQL). + value: + consumer: alerts + name: my Elasticsearch query KQL rule params: aggType: count excludeHitsFromPreviousRun: true @@ -4650,11 +4740,76 @@ components: tags: - cpu create_es_query_rule_response: + summary: The create rule API returns a JSON object that contains details about the rule. + value: + id: 58148c70-407f-11ee-850e-c71febc4ca7f + enabled: true + name: my Elasticsearch query rule + tags: [] + rule_type_id: .es-query + consumer: alerts + schedule: + interval: 1d + actions: + - group: query matched + id: fdbece50-406c-11ee-850e-c71febc4ca7f + params: + level: info + message: The system has detected {{alerts.new.count}} new, {{alerts.ongoing.count}} ongoing, and {{alerts.recovered.count}} recovered alerts. + connector_type_id: .server-log + frequency: + summary: true + notify_when: onThrottleInterval + throttle: 1d + uuid: 53f3c2a3-e5d0-4cfa-af3b-6f0881385e78 + - group: recovered + id: fdbece50-406c-11ee-850e-c71febc4ca7f + params: + level: info + message: Recovered + connector_type_id: .server-log + frequency: + summary: false + notify_when: onActionGroupChange + throttle: null + uuid: 2324e45b-c0df-45c7-9d70-4993e30be758 + params: + thresholdComparator: '>' + timeWindowSize: 1 + timeWindowUnit: d + threshold: + - 100 + size: 100 + timeField: '@timestamp' + index: + - kibana_sample_data_logs + esQuery: '"""{"query":{"match_all" : {}}}"""' + excludeHitsFromPreviousRun: true + aggType: count + groupBy: all + searchType: esQuery + scheduled_task_id: 58148c70-407f-11ee-850e-c71febc4ca7f + created_by: elastic + updated_by: elastic + created_at: '2023-08-22T00:03:38.263Z' + updated_at: '2023-08-22T00:03:38.263Z' + api_key_owner: elastic + api_key_created_by_user: false + throttle: null + mute_all: false + notify_when: null + muted_alert_ids: [] + execution_status: + status: pending + last_execution_date: '2023-08-22T00:03:38.263Z' + revision: 0 + running: false + create_es_query_kql_rule_response: summary: The create rule API returns a JSON object that contains details about the rule. value: id: 7bd506d0-2284-11ee-8fad-6101956ced88 enabled: true - name: my Elasticsearch query rule" + name: my Elasticsearch query KQL rule" tags: [] rule_type_id: .es-query consumer: alerts @@ -5217,6 +5372,3 @@ components: id: recovered name: Recovered rule_task_timeout: 5m -security: - - basicAuth: [] - - apiKeyAuth: [] diff --git a/x-pack/plugins/alerting/docs/openapi/components/examples/create_es_query_kql_rule_request.yaml b/x-pack/plugins/alerting/docs/openapi/components/examples/create_es_query_kql_rule_request.yaml new file mode 100644 index 0000000000000..e505fd8964463 --- /dev/null +++ b/x-pack/plugins/alerting/docs/openapi/components/examples/create_es_query_kql_rule_request.yaml @@ -0,0 +1,23 @@ +summary: Create an Elasticsearch query rule that uses Kibana query language (KQL). +value: + consumer: alerts + name: my Elasticsearch query KQL rule + params: + aggType: count + excludeHitsFromPreviousRun: true + groupBy: all + searchConfiguration: + query: + query: '""geo.src : "US" ""' + language: kuery + index: 90943e30-9a47-11e8-b64d-95841ca0b247 + searchType: searchSource + size: 100 + threshold: + - 1000 + thresholdComparator: ">" + timeWindowSize: 5 + timeWindowUnit: m + rule_type_id: .es-query + schedule: + interval: 1m diff --git a/x-pack/plugins/alerting/docs/openapi/components/examples/create_es_query_kql_rule_response.yaml b/x-pack/plugins/alerting/docs/openapi/components/examples/create_es_query_kql_rule_response.yaml new file mode 100644 index 0000000000000..0a30c4e6dd41e --- /dev/null +++ b/x-pack/plugins/alerting/docs/openapi/components/examples/create_es_query_kql_rule_response.yaml @@ -0,0 +1,43 @@ +summary: The create rule API returns a JSON object that contains details about the rule. +value: + id: 7bd506d0-2284-11ee-8fad-6101956ced88 + enabled: true + name: my Elasticsearch query KQL rule" + tags: [] + rule_type_id: .es-query + consumer: alerts + schedule: + interval: 1m + actions: [] + params: + searchConfiguration: + query: + query: '""geo.src : "US" ""' + language: kuery + index: 90943e30-9a47-11e8-b64d-95841ca0b247 + searchType: searchSource + timeWindowSize: 5 + timeWindowUnit: m + threshold: + - 1000 + thresholdComparator: ">" + size: 100 + aggType: count + groupBy: all + excludeHitsFromPreviousRun: true + created_by: elastic + updated_by: elastic + created_at: '2023-07-14T20:24:50.729Z' + updated_at: '2023-07-14T20:24:50.729Z' + api_key_owner: elastic + api_key_created_by_user: false + throttle: null + notify_when: null + mute_all: false + muted_alert_ids: [] + scheduled_task_id: 7bd506d0-2284-11ee-8fad-6101956ced88 + execution_status: + status: pending + last_execution_date: '2023-07-14T20:24:50.729Z' + revision: 0 + running: false \ No newline at end of file diff --git a/x-pack/plugins/alerting/docs/openapi/components/examples/create_es_query_rule_request.yaml b/x-pack/plugins/alerting/docs/openapi/components/examples/create_es_query_rule_request.yaml index b17f6626b34dc..bff7a8f0bd8f6 100644 --- a/x-pack/plugins/alerting/docs/openapi/components/examples/create_es_query_rule_request.yaml +++ b/x-pack/plugins/alerting/docs/openapi/components/examples/create_es_query_rule_request.yaml @@ -1,23 +1,36 @@ -summary: Create an Elasticsearch query rule. +summary: Create an Elasticsearch query rule that uses Elasticsearch query domain specific language (DSL) to define its query and a server log connector to send notifications. value: + actions: + - group: query matched + params: + level: info + message: "The system has detected {{alerts.new.count}} new, {{alerts.ongoing.count}} ongoing, and {{alerts.recovered.count}} recovered alerts." + id: fdbece50-406c-11ee-850e-c71febc4ca7f + frequency: + throttle: "1d" + summary: true + notify_when: onThrottleInterval + - group: recovered + params: + level: info + message: Recovered + id: fdbece50-406c-11ee-850e-c71febc4ca7f + frequency: + summary: false + notify_when: onActionGroupChange consumer: alerts name: my Elasticsearch query rule - params: - aggType: count - excludeHitsFromPreviousRun: true - groupBy: all - searchConfiguration: - query: - query: '""geo.src : "US" ""' - language: kuery - index: 90943e30-9a47-11e8-b64d-95841ca0b247 - searchType: searchSource + params: + esQuery: '"""{"query":{"match_all" : {}}}"""' + index: + - kibana_sample_data_logs size: 100 threshold: - - 1000 + - 100 thresholdComparator: ">" - timeWindowSize: 5 - timeWindowUnit: m + timeField: "@timestamp" + timeWindowSize: 1 + timeWindowUnit: d rule_type_id: .es-query schedule: - interval: 1m + interval: 1d \ No newline at end of file diff --git a/x-pack/plugins/alerting/docs/openapi/components/examples/create_es_query_rule_response.yaml b/x-pack/plugins/alerting/docs/openapi/components/examples/create_es_query_rule_response.yaml index 5f24e00421a6f..9601843a42e3b 100644 --- a/x-pack/plugins/alerting/docs/openapi/components/examples/create_es_query_rule_response.yaml +++ b/x-pack/plugins/alerting/docs/openapi/components/examples/create_es_query_rule_response.yaml @@ -1,43 +1,64 @@ summary: The create rule API returns a JSON object that contains details about the rule. value: - id: 7bd506d0-2284-11ee-8fad-6101956ced88 + id: 58148c70-407f-11ee-850e-c71febc4ca7f enabled: true - name: my Elasticsearch query rule" + name: my Elasticsearch query rule tags: [] rule_type_id: .es-query consumer: alerts - schedule: - interval: 1m - actions: [] - params: - searchConfiguration: - query: - query: '""geo.src : "US" ""' - language: kuery - index: 90943e30-9a47-11e8-b64d-95841ca0b247 - searchType: searchSource - timeWindowSize: 5 - timeWindowUnit: m - threshold: - - 1000 + schedule: + interval: 1d + actions: + - group: query matched + id: fdbece50-406c-11ee-850e-c71febc4ca7f + params: + level: info + message: "The system has detected {{alerts.new.count}} new, {{alerts.ongoing.count}} ongoing, and {{alerts.recovered.count}} recovered alerts." + connector_type_id: .server-log + frequency: + summary: true + notify_when: onThrottleInterval + throttle: "1d" + uuid: 53f3c2a3-e5d0-4cfa-af3b-6f0881385e78 + - group: recovered + id: fdbece50-406c-11ee-850e-c71febc4ca7f + params: + level: info + message: Recovered + connector_type_id: .server-log + frequency: + summary: false + notify_when: onActionGroupChange + throttle: null + uuid: 2324e45b-c0df-45c7-9d70-4993e30be758 + params: thresholdComparator: ">" + timeWindowSize: 1 + timeWindowUnit: d + threshold: + - 100 size: 100 + timeField: "@timestamp" + index: + - kibana_sample_data_logs + esQuery: '"""{"query":{"match_all" : {}}}"""' + excludeHitsFromPreviousRun: true aggType: count groupBy: all - excludeHitsFromPreviousRun: true + searchType: esQuery + scheduled_task_id: 58148c70-407f-11ee-850e-c71febc4ca7f created_by: elastic updated_by: elastic - created_at: '2023-07-14T20:24:50.729Z' - updated_at: '2023-07-14T20:24:50.729Z' + created_at: '2023-08-22T00:03:38.263Z' + updated_at: '2023-08-22T00:03:38.263Z' api_key_owner: elastic api_key_created_by_user: false throttle: null - notify_when: null mute_all: false + notify_when: null muted_alert_ids: [] - scheduled_task_id: 7bd506d0-2284-11ee-8fad-6101956ced88 execution_status: status: pending - last_execution_date: '2023-07-14T20:24:50.729Z' + last_execution_date: '2023-08-22T00:03:38.263Z' revision: 0 running: false \ No newline at end of file diff --git a/x-pack/plugins/alerting/docs/openapi/components/schemas/400_response.yaml b/x-pack/plugins/alerting/docs/openapi/components/schemas/400_response.yaml new file mode 100644 index 0000000000000..ab0887586c0eb --- /dev/null +++ b/x-pack/plugins/alerting/docs/openapi/components/schemas/400_response.yaml @@ -0,0 +1,17 @@ +title: Bad request +type: object +required: + - error + - message + - statusCode +properties: + error: + type: string + enum: + - Bad Request + message: + type: string + statusCode: + type: integer + enum: + - 400 \ No newline at end of file diff --git a/x-pack/plugins/alerting/docs/openapi/entrypoint.yaml b/x-pack/plugins/alerting/docs/openapi/entrypoint.yaml index 5e73a74c058a0..f6beba7fdb82c 100644 --- a/x-pack/plugins/alerting/docs/openapi/entrypoint.yaml +++ b/x-pack/plugins/alerting/docs/openapi/entrypoint.yaml @@ -33,6 +33,8 @@ paths: $ref: 'paths/s@{spaceid}@api@alerting@rule@{ruleid}@_mute_all.yaml' '/s/{spaceId}/api/alerting/rule/{ruleId}/_unmute_all': $ref: 'paths/s@{spaceid}@api@alerting@rule@{ruleid}@_unmute_all.yaml' + '/s/{spaceId}/api/alerting/rule/{ruleId}/_update_api_key': + $ref: 'paths/s@{spaceid}@api@alerting@rule@{ruleid}@_update_api_key.yaml' '/s/{spaceId}/api/alerting/rule/{ruleId}/alert/{alertId}/_mute': $ref: 'paths/s@{spaceid}@api@alerting@rule@{ruleid}@alert@{alertid}@_mute.yaml' '/s/{spaceId}/api/alerting/rule/{ruleId}/alert/{alertId}/_unmute': diff --git a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@rule.yaml b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@rule.yaml index 5a7ebd986a234..f88f69a437a9c 100644 --- a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@rule.yaml +++ b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@rule.yaml @@ -23,6 +23,8 @@ post: examples: createEsQueryRuleRequest: $ref: '../components/examples/create_es_query_rule_request.yaml' + createEsQueryKqlRuleRequest: + $ref: '../components/examples/create_es_query_kql_rule_request.yaml' createIndexThresholdRuleRequest: $ref: '../components/examples/create_index_threshold_rule_request.yaml' responses: @@ -35,6 +37,8 @@ post: examples: createEsQueryRuleResponse: $ref: '../components/examples/create_es_query_rule_response.yaml' + createEsQueryKqlRuleResponse: + $ref: '../components/examples/create_es_query_kql_rule_response.yaml' createIndexThresholdRuleResponse: $ref: '../components/examples/create_index_threshold_rule_response.yaml' '401': diff --git a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@rule@{ruleid}.yaml b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@rule@{ruleid}.yaml index 9bfd620d9bfd5..058e825f1aac3 100644 --- a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@rule@{ruleid}.yaml +++ b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@rule@{ruleid}.yaml @@ -104,6 +104,8 @@ post: examples: createEsQueryRuleIdRequest: $ref: '../components/examples/create_es_query_rule_request.yaml' + createEsQueryKqlRuleIdRequest: + $ref: '../components/examples/create_es_query_kql_rule_request.yaml' createIndexThreholdRuleIdRequest: $ref: '../components/examples/create_index_threshold_rule_request.yaml' responses: @@ -116,6 +118,8 @@ post: examples: createEsQueryRuleIdResponse: $ref: '../components/examples/create_es_query_rule_response.yaml' + createEsQueryKqlRuleIdResponse: + $ref: '../components/examples/create_es_query_kql_rule_response.yaml' createIndexThresholdRuleIdResponse: $ref: '../components/examples/create_index_threshold_rule_response.yaml' '401': diff --git a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@rule@{ruleid}@_update_api_key.yaml b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@rule@{ruleid}@_update_api_key.yaml new file mode 100644 index 0000000000000..a4d79f12943cc --- /dev/null +++ b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@rule@{ruleid}@_update_api_key.yaml @@ -0,0 +1,23 @@ +post: + summary: Updates the API key for a rule. + operationId: updateRuleAPIKey + description: The new API key has the credentials of the user that submits the request. + tags: + - alerting + parameters: + - $ref: ../components/headers/kbn_xsrf.yaml + - $ref: '../components/parameters/rule_id.yaml' + - $ref: '../components/parameters/space_id.yaml' + responses: + '200': + description: Indicates a successful call. + '400': + description: Bad request + content: + application/json: + schema: + $ref: '../components/schemas/400_response.yaml' + servers: + - url: https://localhost:5601 +servers: + - url: https://localhost:5601 \ No newline at end of file diff --git a/x-pack/plugins/alerting/server/alert/alert.test.ts b/x-pack/plugins/alerting/server/alert/alert.test.ts index 95894fe440107..c4db2189e6d26 100644 --- a/x-pack/plugins/alerting/server/alert/alert.test.ts +++ b/x-pack/plugins/alerting/server/alert/alert.test.ts @@ -44,7 +44,7 @@ describe('isThrottled', () => { const alert = new Alert('1', { meta: { lastScheduledActions: { - date: new Date(), + date: new Date().toISOString(), group: 'default', }, }, @@ -58,10 +58,10 @@ describe('isThrottled', () => { const alert = new Alert('1', { meta: { lastScheduledActions: { - date: new Date(), + date: new Date().toISOString(), group: 'default', actions: { - 'slack:alert:1h': { date: new Date() }, + 'slack:alert:1h': { date: new Date().toISOString() }, }, }, }, @@ -77,7 +77,7 @@ describe('isThrottled', () => { const alert = new Alert('1', { meta: { lastScheduledActions: { - date: new Date(), + date: new Date().toISOString(), group: 'default', }, }, @@ -91,7 +91,7 @@ describe('isThrottled', () => { const alert = new Alert('1', { meta: { lastScheduledActions: { - date: new Date(), + date: new Date().toISOString(), group: 'default', }, }, @@ -105,10 +105,10 @@ describe('isThrottled', () => { const alert = new Alert('1', { meta: { lastScheduledActions: { - date: new Date(), + date: new Date().toISOString(), group: 'default', actions: { - '111-111': { date: new Date() }, + '111-111': { date: new Date().toISOString() }, }, }, }, @@ -122,10 +122,10 @@ describe('isThrottled', () => { const alert = new Alert('1', { meta: { lastScheduledActions: { - date: new Date('2020-01-01'), + date: new Date('2020-01-01').toISOString(), group: 'default', actions: { - '111-111': { date: new Date() }, + '111-111': { date: new Date().toISOString() }, }, }, }, @@ -141,10 +141,10 @@ describe('isThrottled', () => { const alert = new Alert('1', { meta: { lastScheduledActions: { - date: new Date(), + date: new Date().toISOString(), group: 'default', actions: { - '111-111': { date: new Date() }, + '111-111': { date: new Date().toISOString() }, }, }, }, @@ -165,7 +165,7 @@ describe('scheduledActionGroupHasChanged()', () => { const alert = new Alert('1', { meta: { lastScheduledActions: { - date: new Date(), + date: new Date().toISOString(), group: 'default', }, }, @@ -184,7 +184,7 @@ describe('scheduledActionGroupHasChanged()', () => { const alert = new Alert('1', { meta: { lastScheduledActions: { - date: new Date(), + date: new Date().toISOString(), group: 'default', }, }, @@ -274,7 +274,7 @@ describe('scheduleActions()', () => { state: { foo: true }, meta: { lastScheduledActions: { - date: new Date(), + date: new Date().toISOString(), group: 'default', }, }, @@ -288,7 +288,7 @@ describe('scheduleActions()', () => { state: { foo: true }, meta: { lastScheduledActions: { - date: new Date(), + date: new Date().toISOString(), group: 'default', }, }, @@ -302,7 +302,7 @@ describe('scheduleActions()', () => { state: { foo: true }, meta: { lastScheduledActions: { - date: new Date(), + date: new Date().toISOString(), group: 'default', }, }, @@ -396,10 +396,10 @@ describe('updateLastScheduledActions()', () => { flappingHistory: [], maintenanceWindowIds: [], lastScheduledActions: { - date: new Date(), + date: new Date().toISOString(), group: 'default', actions: { - 'slack:alert:1h': { date: new Date() }, + 'slack:alert:1h': { date: new Date().toISOString() }, }, }, }, @@ -429,7 +429,7 @@ describe('getContext()', () => { state: { foo: true }, meta: { lastScheduledActions: { - date: new Date(), + date: new Date().toISOString(), group: 'default', }, }, @@ -442,7 +442,7 @@ describe('getContext()', () => { state: { foo: true }, meta: { lastScheduledActions: { - date: new Date(), + date: new Date().toISOString(), group: 'default', }, }, @@ -458,7 +458,7 @@ describe('hasContext()', () => { state: { foo: true }, meta: { lastScheduledActions: { - date: new Date(), + date: new Date().toISOString(), group: 'default', }, }, @@ -472,7 +472,7 @@ describe('hasContext()', () => { state: { foo: true }, meta: { lastScheduledActions: { - date: new Date(), + date: new Date().toISOString(), group: 'default', }, }, @@ -486,7 +486,7 @@ describe('hasContext()', () => { state: { foo: true }, meta: { lastScheduledActions: { - date: new Date(), + date: new Date().toISOString(), group: 'default', }, }, @@ -503,7 +503,7 @@ describe('toJSON', () => { state: { foo: true }, meta: { lastScheduledActions: { - date: new Date(), + date: new Date().toISOString(), group: 'default', }, flappingHistory: [false, true], @@ -520,7 +520,7 @@ describe('toJSON', () => { }, meta: { lastScheduledActions: { - date: expect.any(Date), + date: expect.any(String), group: 'default', }, uuid: expect.any(String), @@ -538,7 +538,7 @@ describe('toRaw', () => { state: { foo: true }, meta: { lastScheduledActions: { - date: new Date(), + date: new Date().toISOString(), group: 'default', }, flappingHistory: [false, true, true], @@ -557,7 +557,7 @@ describe('toRaw', () => { state: { foo: true }, meta: { lastScheduledActions: { - date: new Date(), + date: new Date().toISOString(), group: 'default', }, flappingHistory: [false, true, true], diff --git a/x-pack/plugins/alerting/server/alert/alert.ts b/x-pack/plugins/alerting/server/alert/alert.ts index 4e08a314bb3a4..b05c3bea22cc3 100644 --- a/x-pack/plugins/alerting/server/alert/alert.ts +++ b/x-pack/plugins/alerting/server/alert/alert.ts @@ -7,12 +7,12 @@ import { v4 as uuidV4 } from 'uuid'; import { isEmpty } from 'lodash'; +import { MutableAlertInstanceMeta } from '@kbn/alerting-state-types'; import { AlertHit, CombinedSummarizedAlerts } from '../types'; import { AlertInstanceMeta, AlertInstanceState, RawAlertInstance, - rawAlertInstance, AlertInstanceContext, DefaultActionGroupId, LastScheduledActions, @@ -52,7 +52,7 @@ export class Alert< ActionGroupIds extends string = never > { private scheduledExecutionOptions?: ScheduledExecutionOptions; - private meta: AlertInstanceMeta; + private meta: MutableAlertInstanceMeta; private state: State; private context: Context; private readonly id: string; @@ -111,11 +111,13 @@ export class Alert< this.meta.lastScheduledActions.actions[uuid] || this.meta.lastScheduledActions.actions[actionHash]; // actionHash must be removed once all the hash identifiers removed from the task state const lastTriggerDate = actionInState?.date; - return !!(lastTriggerDate && lastTriggerDate.getTime() + throttleMills > Date.now()); + return !!( + lastTriggerDate && new Date(lastTriggerDate).getTime() + throttleMills > Date.now() + ); } return false; } else { - return this.meta.lastScheduledActions.date.getTime() + throttleMills > Date.now(); + return new Date(this.meta.lastScheduledActions.date).getTime() + throttleMills > Date.now(); } } return false; @@ -202,7 +204,7 @@ export class Alert< if (!this.meta.lastScheduledActions) { this.meta.lastScheduledActions = {} as LastScheduledActions; } - const date = new Date(); + const date = new Date().toISOString(); this.meta.lastScheduledActions.group = group; this.meta.lastScheduledActions.date = date; @@ -224,7 +226,7 @@ export class Alert< * Used to serialize alert instance state */ toJSON() { - return rawAlertInstance.encode(this.toRaw()); + return this.toRaw(); } toRaw(recovered: boolean = false): RawAlertInstance { diff --git a/x-pack/plugins/alerting/server/alert/create_alert_factory.test.ts b/x-pack/plugins/alerting/server/alert/create_alert_factory.test.ts index dc9c09269403a..c991ed961a89c 100644 --- a/x-pack/plugins/alerting/server/alert/create_alert_factory.test.ts +++ b/x-pack/plugins/alerting/server/alert/create_alert_factory.test.ts @@ -49,7 +49,10 @@ describe('createAlertFactory()', () => { test('reuses existing alerts', () => { const alert = new Alert('1', { state: { foo: true }, - meta: { lastScheduledActions: { group: 'default', date: new Date() }, uuid: 'uuid-previous' }, + meta: { + lastScheduledActions: { group: 'default', date: new Date().toISOString() }, + uuid: 'uuid-previous', + }, }); const alertFactory = createAlertFactory({ alerts: { @@ -65,7 +68,7 @@ describe('createAlertFactory()', () => { uuid: 'uuid-previous', flappingHistory: [], lastScheduledActions: { - date: expect.any(Date), + date: expect.any(String), group: 'default', }, }, @@ -100,7 +103,10 @@ describe('createAlertFactory()', () => { test('gets alert if it exists, returns null if it does not', () => { const alert = new Alert('1', { state: { foo: true }, - meta: { lastScheduledActions: { group: 'default', date: new Date() }, uuid: 'uuid-previous' }, + meta: { + lastScheduledActions: { group: 'default', date: new Date().toISOString() }, + uuid: 'uuid-previous', + }, }); const alertFactory = createAlertFactory({ alerts: { diff --git a/x-pack/plugins/alerting/server/alerts_client/alerts_client.test.ts b/x-pack/plugins/alerting/server/alerts_client/alerts_client.test.ts index 06b90dd675b48..601732a607900 100644 --- a/x-pack/plugins/alerting/server/alerts_client/alerts_client.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/alerts_client.test.ts @@ -177,7 +177,7 @@ describe('Alerts Client', () => { meta: { flapping: false, flappingHistory: [true, false], - lastScheduledActions: { group: 'default', date: new Date() }, + lastScheduledActions: { group: 'default', date: new Date().toISOString() }, uuid: 'abc', }, }), @@ -186,7 +186,7 @@ describe('Alerts Client', () => { meta: { flapping: false, flappingHistory: [true, false, false], - lastScheduledActions: { group: 'default', date: new Date() }, + lastScheduledActions: { group: 'default', date: new Date().toISOString() }, uuid: 'def', }, }), @@ -245,7 +245,7 @@ describe('Alerts Client', () => { meta: { flapping: false, flappingHistory: [true, false], - lastScheduledActions: { group: 'default', date: new Date() }, + lastScheduledActions: { group: 'default', date: new Date().toISOString() }, uuid: id, }, }); @@ -285,7 +285,7 @@ describe('Alerts Client', () => { meta: { flapping: false, flappingHistory: [true, false], - lastScheduledActions: { group: 'default', date: new Date() }, + lastScheduledActions: { group: 'default', date: new Date().toISOString() }, uuid: 'abc', }, }), @@ -540,7 +540,7 @@ describe('Alerts Client', () => { flapping: false, flappingHistory: [true], maintenanceWindowIds: [], - lastScheduledActions: { group: 'default', date: new Date() }, + lastScheduledActions: { group: 'default', date: new Date().toISOString() }, uuid: 'abc', }, }, @@ -800,7 +800,7 @@ describe('Alerts Client', () => { flapping: false, flappingHistory: [true], maintenanceWindowIds: [], - lastScheduledActions: { group: 'default', date: new Date() }, + lastScheduledActions: { group: 'default', date: new Date().toISOString() }, uuid: 'abc', }, }, @@ -810,7 +810,7 @@ describe('Alerts Client', () => { flapping: false, flappingHistory: [true, false], maintenanceWindowIds: [], - lastScheduledActions: { group: 'default', date: new Date() }, + lastScheduledActions: { group: 'default', date: new Date().toISOString() }, uuid: 'def', }, }, @@ -1779,7 +1779,7 @@ describe('Alerts Client', () => { flapping: false, flappingHistory: [true], maintenanceWindowIds: [], - lastScheduledActions: { group: 'default', date: new Date() }, + lastScheduledActions: { group: 'default', date: new Date().toISOString() }, uuid: 'abc', }, }, @@ -1789,7 +1789,7 @@ describe('Alerts Client', () => { flapping: false, flappingHistory: [true, false], maintenanceWindowIds: [], - lastScheduledActions: { group: 'default', date: new Date() }, + lastScheduledActions: { group: 'default', date: new Date().toISOString() }, uuid: 'def', }, }, @@ -1827,7 +1827,7 @@ describe('Alerts Client', () => { flapping: false, flappingHistory: [true], maintenanceWindowIds: [], - lastScheduledActions: { group: 'default', date: new Date() }, + lastScheduledActions: { group: 'default', date: new Date().toISOString() }, uuid: 'abc', }, }, @@ -1837,7 +1837,7 @@ describe('Alerts Client', () => { flapping: false, flappingHistory: [true, false], maintenanceWindowIds: [], - lastScheduledActions: { group: 'default', date: new Date() }, + lastScheduledActions: { group: 'default', date: new Date().toISOString() }, uuid: 'def', }, }, @@ -2050,7 +2050,7 @@ describe('Alerts Client', () => { flapping: false, flappingHistory: [true], maintenanceWindowIds: [], - lastScheduledActions: { group: 'default', date: new Date() }, + lastScheduledActions: { group: 'default', date: new Date().toISOString() }, uuid: 'abc', }, }, @@ -2227,7 +2227,7 @@ describe('Alerts Client', () => { flapping: false, flappingHistory: [true], maintenanceWindowIds: [], - lastScheduledActions: { group: 'default', date: new Date() }, + lastScheduledActions: { group: 'default', date: new Date().toISOString() }, uuid: 'abc', }, }, @@ -2415,7 +2415,7 @@ describe('Alerts Client', () => { flapping: false, flappingHistory: [true], maintenanceWindowIds: [], - lastScheduledActions: { group: 'default', date: new Date() }, + lastScheduledActions: { group: 'default', date: new Date().toISOString() }, uuid: 'abc', }, }, @@ -2505,7 +2505,7 @@ describe('Alerts Client', () => { flapping: false, flappingHistory: [true], maintenanceWindowIds: [], - lastScheduledActions: { group: 'default', date: new Date() }, + lastScheduledActions: { group: 'default', date: new Date().toISOString() }, uuid: 'abc', }, }, diff --git a/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.test.ts b/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.test.ts index 89f1a9469c2ea..f8c341e132e51 100644 --- a/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.test.ts @@ -110,7 +110,7 @@ const testAlert2 = { meta: { lastScheduledActions: { group: 'default', - date: new Date(), + date: new Date().toISOString(), }, uuid: 'def', }, diff --git a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts index b5fff8a84c38c..007cd4481bd7e 100644 --- a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts +++ b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts @@ -7,6 +7,7 @@ import { eventLoggerMock } from '@kbn/event-log-plugin/server/event_logger.mock'; import { IEvent, SAVED_OBJECT_REL_PRIMARY } from '@kbn/event-log-plugin/server'; +import { ActionsCompletion } from '@kbn/alerting-state-types'; import { AlertingEventLogger, RuleContextOpts, @@ -19,7 +20,6 @@ import { } from './alerting_event_logger'; import { UntypedNormalizedRuleType } from '../../rule_type_registry'; import { - ActionsCompletion, RecoveredActionGroup, RuleExecutionStatusErrorReasons, RuleExecutionStatusWarningReasons, diff --git a/x-pack/plugins/alerting/server/lib/last_run_status.test.ts b/x-pack/plugins/alerting/server/lib/last_run_status.test.ts index d6b6f62b1b60a..33af749fe1e08 100644 --- a/x-pack/plugins/alerting/server/lib/last_run_status.test.ts +++ b/x-pack/plugins/alerting/server/lib/last_run_status.test.ts @@ -5,8 +5,8 @@ * 2.0. */ +import { ActionsCompletion } from '@kbn/alerting-state-types'; import { lastRunFromState } from './last_run_status'; -import { ActionsCompletion } from '../../common'; import { RuleRunMetrics } from './rule_run_metrics_store'; import { RuleResultServiceResults, RuleResultService } from '../monitoring/rule_result_service'; diff --git a/x-pack/plugins/alerting/server/lib/last_run_status.ts b/x-pack/plugins/alerting/server/lib/last_run_status.ts index a007d8637eac0..56da93f074c27 100644 --- a/x-pack/plugins/alerting/server/lib/last_run_status.ts +++ b/x-pack/plugins/alerting/server/lib/last_run_status.ts @@ -5,10 +5,11 @@ * 2.0. */ +import { ActionsCompletion } from '@kbn/alerting-state-types'; import { RuleTaskStateAndMetrics } from '../task_runner/types'; import { getReasonFromError } from './error_with_reason'; import { getEsErrorMessage } from './errors'; -import { ActionsCompletion, RuleLastRunOutcomeOrderMap, RuleLastRunOutcomes } from '../../common'; +import { RuleLastRunOutcomeOrderMap, RuleLastRunOutcomes } from '../../common'; import { RuleLastRunOutcomeValues, RuleExecutionStatusWarningReasons, diff --git a/x-pack/plugins/alerting/server/lib/rule_execution_status.test.ts b/x-pack/plugins/alerting/server/lib/rule_execution_status.test.ts index 69c90ed812549..0210de56a6b0d 100644 --- a/x-pack/plugins/alerting/server/lib/rule_execution_status.test.ts +++ b/x-pack/plugins/alerting/server/lib/rule_execution_status.test.ts @@ -6,11 +6,8 @@ */ import { loggingSystemMock } from '@kbn/core/server/mocks'; -import { - ActionsCompletion, - RuleExecutionStatusErrorReasons, - RuleExecutionStatusWarningReasons, -} from '../types'; +import { ActionsCompletion } from '@kbn/alerting-state-types'; +import { RuleExecutionStatusErrorReasons, RuleExecutionStatusWarningReasons } from '../types'; import { executionStatusFromState, executionStatusFromError, diff --git a/x-pack/plugins/alerting/server/lib/rule_execution_status.ts b/x-pack/plugins/alerting/server/lib/rule_execution_status.ts index fbcf5d9ec5f2a..43ab9e2153a94 100644 --- a/x-pack/plugins/alerting/server/lib/rule_execution_status.ts +++ b/x-pack/plugins/alerting/server/lib/rule_execution_status.ts @@ -6,6 +6,7 @@ */ import { Logger } from '@kbn/core/server'; +import { ActionsCompletion } from '@kbn/alerting-state-types'; import { RuleExecutionStatus, RuleExecutionStatusValues, @@ -16,7 +17,7 @@ import { } from '../types'; import { getReasonFromError } from './error_with_reason'; import { getEsErrorMessage } from './errors'; -import { ActionsCompletion, RuleExecutionStatuses } from '../../common'; +import { RuleExecutionStatuses } from '../../common'; import { translations } from '../constants/translations'; import { RuleTaskStateAndMetrics } from '../task_runner/types'; import { RuleRunMetrics } from './rule_run_metrics_store'; diff --git a/x-pack/plugins/alerting/server/lib/rule_run_metrics_store.test.ts b/x-pack/plugins/alerting/server/lib/rule_run_metrics_store.test.ts index a042df442b787..8f2410480cc6f 100644 --- a/x-pack/plugins/alerting/server/lib/rule_run_metrics_store.test.ts +++ b/x-pack/plugins/alerting/server/lib/rule_run_metrics_store.test.ts @@ -5,8 +5,8 @@ * 2.0. */ +import { ActionsCompletion } from '@kbn/alerting-state-types'; import { RuleRunMetricsStore } from './rule_run_metrics_store'; -import { ActionsCompletion } from '../types'; describe('RuleRunMetricsStore', () => { const ruleRunMetricsStore = new RuleRunMetricsStore(); diff --git a/x-pack/plugins/alerting/server/lib/rule_run_metrics_store.ts b/x-pack/plugins/alerting/server/lib/rule_run_metrics_store.ts index db83aeb7a63d6..14879e1558ba6 100644 --- a/x-pack/plugins/alerting/server/lib/rule_run_metrics_store.ts +++ b/x-pack/plugins/alerting/server/lib/rule_run_metrics_store.ts @@ -6,7 +6,7 @@ */ import { set } from '@kbn/safer-lodash-set'; -import { ActionsCompletion } from '../types'; +import { ActionsCompletion } from '@kbn/alerting-state-types'; import { ActionsConfigMap } from './get_actions_config_map'; import { SearchMetrics } from './types'; diff --git a/x-pack/plugins/alerting/server/lib/types.test.ts b/x-pack/plugins/alerting/server/lib/types.test.ts deleted file mode 100644 index b3f61a0f2172c..0000000000000 --- a/x-pack/plugins/alerting/server/lib/types.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { DateFromString } from './types'; -import { right, isLeft } from 'fp-ts/lib/Either'; - -describe('DateFromString', () => { - test('validated and parses a string into a Date', () => { - const date = new Date(1973, 10, 30); - expect(DateFromString.decode(date.toISOString())).toEqual(right(date)); - }); - - test('validated and returns a failure for an actual Date', () => { - const date = new Date(1973, 10, 30); - expect(isLeft(DateFromString.decode(date))).toEqual(true); - }); - - test('validated and returns a failure for an invalid Date string', () => { - expect(isLeft(DateFromString.decode('1234-23-45'))).toEqual(true); - }); - - test('validated and returns a failure for a null value', () => { - expect(isLeft(DateFromString.decode(null))).toEqual(true); - }); -}); diff --git a/x-pack/plugins/alerting/server/lib/types.ts b/x-pack/plugins/alerting/server/lib/types.ts index 173ba1119a72a..aa5c13c3fb564 100644 --- a/x-pack/plugins/alerting/server/lib/types.ts +++ b/x-pack/plugins/alerting/server/lib/types.ts @@ -5,29 +5,9 @@ * 2.0. */ -import * as t from 'io-ts'; -import { either } from 'fp-ts/lib/Either'; import { Rule } from '../types'; import { RuleRunMetrics } from './rule_run_metrics_store'; -// represents a Date from an ISO string -export const DateFromString = new t.Type( - 'DateFromString', - // detect the type - (value): value is Date => value instanceof Date, - (valueToDecode, context) => - either.chain( - // validate this is a string - t.string.validate(valueToDecode, context), - // decode - (value) => { - const decoded = new Date(value); - return isNaN(decoded.getTime()) ? t.failure(valueToDecode, context) : t.success(decoded); - } - ), - (valueToEncode) => valueToEncode.toISOString() -); - export type RuleInfo = Pick & { spaceId: string }; export interface LogSearchMetricsOpts { diff --git a/x-pack/plugins/alerting/server/routes/get_rule_state.test.ts b/x-pack/plugins/alerting/server/routes/get_rule_state.test.ts index 04d3704c88714..d84c6fe73b616 100644 --- a/x-pack/plugins/alerting/server/routes/get_rule_state.test.ts +++ b/x-pack/plugins/alerting/server/routes/get_rule_state.test.ts @@ -32,7 +32,7 @@ describe('getRuleStateRoute', () => { meta: { lastScheduledActions: { group: 'first_group', - date: new Date(), + date: new Date().toISOString(), }, }, }, diff --git a/x-pack/plugins/alerting/server/routes/legacy/get_alert_state.test.ts b/x-pack/plugins/alerting/server/routes/legacy/get_alert_state.test.ts index 42527256925f7..67fd50a965ea4 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/get_alert_state.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/get_alert_state.test.ts @@ -37,7 +37,7 @@ describe('getAlertStateRoute', () => { meta: { lastScheduledActions: { group: 'first_group', - date: new Date(), + date: new Date().toISOString(), }, }, }, diff --git a/x-pack/plugins/alerting/server/routes/update_rule_api_key.test.ts b/x-pack/plugins/alerting/server/routes/update_rule_api_key.test.ts index 4b2847eb16f73..ea88e2eba19bd 100644 --- a/x-pack/plugins/alerting/server/routes/update_rule_api_key.test.ts +++ b/x-pack/plugins/alerting/server/routes/update_rule_api_key.test.ts @@ -30,7 +30,7 @@ describe('updateRuleApiKeyRoute', () => { const [config, handler] = router.post.mock.calls[0]; - expect(config.path).toMatchInlineSnapshot(`"/internal/alerting/rule/{id}/_update_api_key"`); + expect(config.path).toMatchInlineSnapshot(`"/api/alerting/rule/{id}/_update_api_key"`); rulesClient.updateApiKey.mockResolvedValueOnce(); diff --git a/x-pack/plugins/alerting/server/routes/update_rule_api_key.ts b/x-pack/plugins/alerting/server/routes/update_rule_api_key.ts index 4e99b54e76af4..6ba598fe621e0 100644 --- a/x-pack/plugins/alerting/server/routes/update_rule_api_key.ts +++ b/x-pack/plugins/alerting/server/routes/update_rule_api_key.ts @@ -9,7 +9,7 @@ import { IRouter } from '@kbn/core/server'; import { schema } from '@kbn/config-schema'; import { ILicenseState, RuleTypeDisabledError } from '../lib'; import { verifyAccessAndContext } from './lib'; -import { AlertingRequestHandlerContext, INTERNAL_BASE_ALERTING_API_PATH } from '../types'; +import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../types'; const paramSchema = schema.object({ id: schema.string(), @@ -21,7 +21,7 @@ export const updateRuleApiKeyRoute = ( ) => { router.post( { - path: `${INTERNAL_BASE_ALERTING_API_PATH}/rule/{id}/_update_api_key`, + path: `${BASE_ALERTING_API_PATH}/rule/{id}/_update_api_key`, validate: { params: paramSchema, }, diff --git a/x-pack/plugins/alerting/server/rule_type_registry.test.ts b/x-pack/plugins/alerting/server/rule_type_registry.test.ts index 353435257708b..689741c7479ac 100644 --- a/x-pack/plugins/alerting/server/rule_type_registry.test.ts +++ b/x-pack/plugins/alerting/server/rule_type_registry.test.ts @@ -17,7 +17,6 @@ import { inMemoryMetricsMock } from './monitoring/in_memory_metrics.mock'; import { alertsServiceMock } from './alerts_service/alerts_service.mock'; import { schema } from '@kbn/config-schema'; import { RecoveredActionGroupId } from '../common'; -import { rawRuleSchema } from './raw_rule_schema'; const logger = loggingSystemMock.create().get(); let mockedLicenseState: jest.Mocked; @@ -437,17 +436,12 @@ describe('Create Lifecycle', () => { const registry = new RuleTypeRegistry(ruleTypeRegistryParams); registry.register(ruleType); expect(taskManager.registerTaskDefinitions).toHaveBeenCalledTimes(1); - expect(taskManager.registerTaskDefinitions.mock.calls[0]).toEqual([ - { - 'alerting:test': { - createTaskRunner: expect.any(Function), - paramsSchema: expect.any(Object), - indirectParamsSchema: rawRuleSchema, - timeout: '20m', - title: 'Test', - }, + expect(taskManager.registerTaskDefinitions.mock.calls[0][0]).toMatchObject({ + 'alerting:test': { + timeout: '20m', + title: 'Test', }, - ]); + }); }); test('shallow clones the given rule type', () => { diff --git a/x-pack/plugins/alerting/server/rule_type_registry.ts b/x-pack/plugins/alerting/server/rule_type_registry.ts index 61600bf2b0955..c0339275bcf35 100644 --- a/x-pack/plugins/alerting/server/rule_type_registry.ts +++ b/x-pack/plugins/alerting/server/rule_type_registry.ts @@ -13,6 +13,7 @@ import { intersection } from 'lodash'; import { Logger } from '@kbn/core/server'; import { LicensingPluginSetup } from '@kbn/licensing-plugin/server'; import { RunContext, TaskManagerSetupContract } from '@kbn/task-manager-plugin/server'; +import { stateSchemaByVersion } from '@kbn/alerting-state-types'; import { rawRuleSchema } from './raw_rule_schema'; import { TaskRunnerFactory } from './task_runner'; import { @@ -279,10 +280,12 @@ export class RuleTypeRegistry { /** stripping the typing is required in order to store the RuleTypes in a Map */ normalizedRuleType as unknown as UntypedNormalizedRuleType ); + this.taskManager.registerTaskDefinitions({ [`alerting:${ruleType.id}`]: { title: ruleType.name, timeout: ruleType.ruleTaskTimeout, + stateSchemaByVersion, createTaskRunner: (context: RunContext) => this.taskRunnerFactory.create< Params, @@ -302,6 +305,7 @@ export class RuleTypeRegistry { indirectParamsSchema: rawRuleSchema, }, }); + if (this.alertsService && ruleType.alerts) { this.alertsService.register(ruleType.alerts as IRuleTypeAlerts); } diff --git a/x-pack/plugins/alerting/server/task_runner/alert_task_instance.test.ts b/x-pack/plugins/alerting/server/task_runner/alert_task_instance.test.ts index 916d9c27a7831..93bb08a55c7d0 100644 --- a/x-pack/plugins/alerting/server/task_runner/alert_task_instance.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/alert_task_instance.test.ts @@ -41,8 +41,7 @@ const alert: SanitizedRule<{ }; describe('Alert Task Instance', () => { - test(`validates that a TaskInstance has valid Alert Task State`, () => { - const lastScheduledActionsDate = new Date(); + test(`passes-through the state object`, () => { const taskInstance: ConcreteTaskInstance = { id: uuidv4(), attempts: 0, @@ -52,129 +51,7 @@ describe('Alert Task Instance', () => { scheduledAt: new Date(), startedAt: new Date(), retryAt: new Date(Date.now() + 5 * 60 * 1000), - state: { - alertTypeState: { - some: 'value', - }, - alertInstances: { - first_instance: { - state: {}, - meta: { - lastScheduledActions: { - group: 'first_group', - date: lastScheduledActionsDate.toISOString(), - }, - }, - }, - second_instance: {}, - }, - }, - taskType: 'alerting:test', - params: { - alertId: '1', - }, - ownerId: null, - }; - - const alertTaskInsatnce: AlertTaskInstance = taskInstanceToAlertTaskInstance(taskInstance); - - expect(alertTaskInsatnce).toEqual({ - ...taskInstance, - state: { - alertTypeState: { - some: 'value', - }, - alertInstances: { - first_instance: { - state: {}, - meta: { - lastScheduledActions: { - group: 'first_group', - date: lastScheduledActionsDate, - }, - }, - }, - second_instance: {}, - }, - }, - }); - }); - - test(`throws if state is invalid`, () => { - const taskInstance: ConcreteTaskInstance = { - id: '215ee69b-1df9-428e-ab1a-ccf274f8fa5b', - attempts: 0, - status: TaskStatus.Running, - version: '123', - runAt: new Date(), - scheduledAt: new Date(), - startedAt: new Date(), - retryAt: new Date(Date.now() + 5 * 60 * 1000), - state: { - alertTypeState: { - some: 'value', - }, - alertInstances: { - first_instance: 'invalid', - second_instance: {}, - }, - }, - taskType: 'alerting:test', - params: { - alertId: '1', - }, - ownerId: null, - }; - - expect(() => taskInstanceToAlertTaskInstance(taskInstance)).toThrowErrorMatchingInlineSnapshot( - `"Task \\"215ee69b-1df9-428e-ab1a-ccf274f8fa5b\\" has invalid state at .alertInstances.first_instance"` - ); - }); - - test(`throws with Alert id when alert is present and state is invalid`, () => { - const taskInstance: ConcreteTaskInstance = { - id: '215ee69b-1df9-428e-ab1a-ccf274f8fa5b', - attempts: 0, - status: TaskStatus.Running, - version: '123', - runAt: new Date(), - scheduledAt: new Date(), - startedAt: new Date(), - retryAt: new Date(Date.now() + 5 * 60 * 1000), - state: { - alertTypeState: { - some: 'value', - }, - alertInstances: { - first_instance: 'invalid', - second_instance: {}, - }, - }, - taskType: 'alerting:test', - params: { - alertId: '1', - }, - ownerId: null, - }; - - expect(() => - taskInstanceToAlertTaskInstance(taskInstance, alert) - ).toThrowErrorMatchingInlineSnapshot( - `"Task \\"215ee69b-1df9-428e-ab1a-ccf274f8fa5b\\" (underlying Alert \\"alert-123\\") has invalid state at .alertInstances.first_instance"` - ); - }); - - test(`allows an initial empty state`, () => { - const taskInstance: ConcreteTaskInstance = { - id: uuidv4(), - attempts: 0, - status: TaskStatus.Running, - version: '123', - runAt: new Date(), - scheduledAt: new Date(), - startedAt: new Date(), - retryAt: new Date(Date.now() + 5 * 60 * 1000), - state: {}, + state: { foo: true }, taskType: 'alerting:test', params: { alertId: '1', diff --git a/x-pack/plugins/alerting/server/task_runner/alert_task_instance.ts b/x-pack/plugins/alerting/server/task_runner/alert_task_instance.ts index d831fe8c63b07..adcf228dde783 100644 --- a/x-pack/plugins/alerting/server/task_runner/alert_task_instance.ts +++ b/x-pack/plugins/alerting/server/task_runner/alert_task_instance.ts @@ -9,14 +9,8 @@ import * as t from 'io-ts'; import { pipe } from 'fp-ts/lib/pipeable'; import { fold } from 'fp-ts/lib/Either'; import { ConcreteTaskInstance } from '@kbn/task-manager-plugin/server'; -import { - SanitizedRule, - RuleTaskState, - ruleParamsSchema, - ruleStateSchema, - RuleTaskParams, - RuleTypeParams, -} from '../../common'; +import { ruleParamsSchema } from '@kbn/alerting-state-types'; +import { SanitizedRule, RuleTaskState, RuleTaskParams, RuleTypeParams } from '../../common'; export interface AlertTaskInstance extends ConcreteTaskInstance { state: RuleTaskState; @@ -42,15 +36,6 @@ export function taskInstanceToAlertTaskInstance( ); }, t.identity) ), - state: pipe( - ruleStateSchema.decode(taskInstance.state), - fold((e: t.Errors) => { - throw new Error( - `Task "${taskInstance.id}" ${ - alert ? `(underlying Alert "${alert.id}") ` : '' - }has invalid state at ${enumerateErrorFields(e)}` - ); - }, t.identity) - ), + state: taskInstance.state as RuleTaskState, }; } diff --git a/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts b/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts index 9f4b33dfd1723..ce86dd4756093 100644 --- a/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts @@ -13,10 +13,10 @@ import { renderActionParameterTemplatesDefault, } from '@kbn/actions-plugin/server/mocks'; import { KibanaRequest } from '@kbn/core/server'; +import { ActionsCompletion } from '@kbn/alerting-state-types'; import { InjectActionParamsOpts, injectActionParams } from './inject_action_params'; import { NormalizedRuleType } from '../rule_type_registry'; import { - ActionsCompletion, ThrottledActions, RuleTypeParams, RuleTypeState, @@ -166,7 +166,7 @@ const generateAlert = ({ meta: { maintenanceWindowIds, lastScheduledActions: { - date: new Date(), + date: new Date().toISOString(), group: lastScheduledActionsGroup, actions: throttledActions, }, @@ -188,7 +188,7 @@ const generateRecoveredAlert = ({ id, state }: { id: number; state?: AlertInstan state: state || { test: true }, meta: { lastScheduledActions: { - date: new Date(), + date: new Date().toISOString(), group: 'recovered', actions: {}, }, @@ -792,7 +792,7 @@ describe('Execution Handler', () => { await executionHandler.run( generateAlert({ id: 1, - throttledActions: { '111-111': { date: new Date(DATE_1970) } }, + throttledActions: { '111-111': { date: new Date(DATE_1970).toISOString() } }, }) ); @@ -1016,7 +1016,7 @@ describe('Execution Handler', () => { expect(result).toEqual({ throttledSummaryActions: { '111-111': { - date: new Date(), + date: new Date().toISOString(), }, }, }); diff --git a/x-pack/plugins/alerting/server/task_runner/execution_handler.ts b/x-pack/plugins/alerting/server/task_runner/execution_handler.ts index f4d8a3151ff2e..6214482ec2706 100644 --- a/x-pack/plugins/alerting/server/task_runner/execution_handler.ts +++ b/x-pack/plugins/alerting/server/task_runner/execution_handler.ts @@ -11,6 +11,7 @@ import { getRuleDetailsRoute, triggersActionsRoute } from '@kbn/rule-data-utils' import { asSavedObjectExecutionSource } from '@kbn/actions-plugin/server'; import { isEphemeralTaskRejectedDueToCapacityError } from '@kbn/task-manager-plugin/server'; import { ExecuteOptions as EnqueueExecutionOptions } from '@kbn/actions-plugin/server/create_execute_function'; +import { ActionsCompletion } from '@kbn/alerting-state-types'; import { ActionsClient } from '@kbn/actions-plugin/server/actions_client'; import { chunk } from 'lodash'; import { GetSummarizedAlertsParams, IAlertsClient } from '../alerts_client/types'; @@ -24,7 +25,6 @@ import { transformActionParams, transformSummaryActionParams } from './transform import { Alert } from '../alert'; import { NormalizedRuleType } from '../rule_type_registry'; import { - ActionsCompletion, AlertInstanceContext, AlertInstanceState, RuleAction, @@ -259,7 +259,7 @@ export class ExecutionHandler< }); if (isActionOnInterval(action)) { - throttledSummaryActions[action.uuid!] = { date: new Date() }; + throttledSummaryActions[action.uuid!] = { date: new Date().toISOString() }; } logActions.push({ diff --git a/x-pack/plugins/alerting/server/task_runner/fixtures.ts b/x-pack/plugins/alerting/server/task_runner/fixtures.ts index 9c7c528903c88..467d7460afc2b 100644 --- a/x-pack/plugins/alerting/server/task_runner/fixtures.ts +++ b/x-pack/plugins/alerting/server/task_runner/fixtures.ts @@ -383,7 +383,7 @@ export const generateRunnerResult = ({ ...(state && { alertInstances }), ...(state && { alertRecoveredInstances }), ...(state && { alertTypeState: {} }), - ...(state && { previousStartedAt: new Date('1970-01-01T00:00:00.000Z') }), + ...(state && { previousStartedAt: new Date('1970-01-01T00:00:00.000Z').toISOString() }), ...(state && { summaryActions }), }, hasError, @@ -440,7 +440,7 @@ export const generateAlertInstance = ( meta: { uuid: expect.any(String), lastScheduledActions: { - date: new Date(DATE_1970), + date: new Date(DATE_1970).toISOString(), group: 'default', ...(actions && { actions }), }, diff --git a/x-pack/plugins/alerting/server/task_runner/rule_action_helper.test.ts b/x-pack/plugins/alerting/server/task_runner/rule_action_helper.test.ts index 872672801b52c..c2ac1ab38a2fa 100644 --- a/x-pack/plugins/alerting/server/task_runner/rule_action_helper.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/rule_action_helper.test.ts @@ -146,21 +146,21 @@ describe('rule_action_helper', () => { const result = getSummaryActionsFromTaskState({ actions: [mockSummaryAction], summaryActions: { - '111-111': { date: new Date('01.01.2020') }, - '222-222': { date: new Date('01.01.2020') }, + '111-111': { date: new Date('01.01.2020').toISOString() }, + '222-222': { date: new Date('01.01.2020').toISOString() }, }, }); - expect(result).toEqual({ '111-111': { date: new Date('01.01.2020') } }); + expect(result).toEqual({ '111-111': { date: new Date('01.01.2020').toISOString() } }); }); test('should replace hash with uuid', () => { const result = getSummaryActionsFromTaskState({ actions: [mockSummaryAction], summaryActions: { - 'slack:summary:1d': { date: new Date('01.01.2020') }, + 'slack:summary:1d': { date: new Date('01.01.2020').toISOString() }, }, }); - expect(result).toEqual({ '111-111': { date: new Date('01.01.2020') } }); + expect(result).toEqual({ '111-111': { date: new Date('01.01.2020').toISOString() } }); }); }); @@ -180,7 +180,7 @@ describe('rule_action_helper', () => { jest.useRealTimers(); }); const logger = { debug: jest.fn() } as unknown as Logger; - const throttledSummaryActions = { '111-111': { date: new Date('2020-01-01T00:00:00.000Z') } }; + const throttledSummaryActions = { '111-111': { date: '2020-01-01T00:00:00.000Z' } }; test('should return false if the action does not have throttle filed', () => { const result = isSummaryActionThrottled({ @@ -227,7 +227,7 @@ describe('rule_action_helper', () => { test('should return false if the action is not in the task instance', () => { const result = isSummaryActionThrottled({ action: mockSummaryAction, - throttledSummaryActions: { '123-456': { date: new Date('2020-01-01T00:00:00.000Z') } }, + throttledSummaryActions: { '123-456': { date: '2020-01-01T00:00:00.000Z' } }, logger, }); expect(result).toBe(false); @@ -237,7 +237,7 @@ describe('rule_action_helper', () => { jest.advanceTimersByTime(3600000 * 2); const result = isSummaryActionThrottled({ action: mockSummaryAction, - throttledSummaryActions: { '123-456': { date: new Date('2020-01-01T00:00:00.000Z') } }, + throttledSummaryActions: { '123-456': { date: '2020-01-01T00:00:00.000Z' } }, logger, }); expect(result).toBe(false); diff --git a/x-pack/plugins/alerting/server/task_runner/rule_action_helper.ts b/x-pack/plugins/alerting/server/task_runner/rule_action_helper.ts index 79fe92b079026..a23323ffee2b7 100644 --- a/x-pack/plugins/alerting/server/task_runner/rule_action_helper.ts +++ b/x-pack/plugins/alerting/server/task_runner/rule_action_helper.ts @@ -58,7 +58,7 @@ export const isSummaryActionThrottled = ({ logger.debug(`Action'${action?.actionTypeId}:${action?.id}', has an invalid throttle interval`); } - const throttled = throttledAction.date.getTime() + throttleMills > Date.now(); + const throttled = new Date(throttledAction.date).getTime() + throttleMills > Date.now(); if (throttled) { logger.debug( diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts index 4e40531a49f2b..fcd2464058350 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts @@ -1565,7 +1565,7 @@ describe('Task Runner', () => { generateEnqueueFunctionInput({ isBulk, id: '1', foo: true }) ); expect(result.state.summaryActions).toEqual({ - '111-111': { date: new Date(DATE_1970) }, + '111-111': { date: new Date(DATE_1970).toISOString() }, }); } ); @@ -1835,9 +1835,7 @@ describe('Task Runner', () => { const runnerResult = await taskRunner.run(); - expect(runnerResult.state.previousStartedAt).toEqual( - new Date(originalAlertSate.previousStartedAt) - ); + expect(runnerResult.state.previousStartedAt).toEqual(originalAlertSate.previousStartedAt); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); @@ -2745,7 +2743,7 @@ describe('Task Runner', () => { meta: { uuid: expect.any(String), lastScheduledActions: { - date: new Date(DATE_1970), + date: new Date(DATE_1970).toISOString(), group: 'default', }, flappingHistory: [true], @@ -2915,7 +2913,7 @@ describe('Task Runner', () => { meta: { uuid: expect.any(String), lastScheduledActions: { - date: new Date(DATE_1970), + date: new Date(DATE_1970).toISOString(), group: 'default', }, flappingHistory: [true], @@ -2932,7 +2930,7 @@ describe('Task Runner', () => { meta: { uuid: expect.any(String), lastScheduledActions: { - date: new Date(DATE_1970), + date: new Date(DATE_1970).toISOString(), group: 'default', }, flappingHistory: [true], diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index 1f0598ce0f69c..6c871f63065a9 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -856,7 +856,7 @@ export class TaskRunner< ): RuleTaskState => { return { ...omit(runStateWithMetrics, ['metrics']), - previousStartedAt: startedAt, + previousStartedAt: startedAt?.toISOString(), }; }; diff --git a/x-pack/plugins/apm/common/utils/formatters/duration.ts b/x-pack/plugins/apm/common/utils/formatters/duration.ts index bf83ed2e093ea..49040f64bb51d 100644 --- a/x-pack/plugins/apm/common/utils/formatters/duration.ts +++ b/x-pack/plugins/apm/common/utils/formatters/duration.ts @@ -31,7 +31,11 @@ export type TimeFormatter = ( options?: FormatterOptions ) => ConvertedDuration; -type TimeFormatterBuilder = (max: number, threshold?: number) => TimeFormatter; +type TimeFormatterBuilder = ( + max: number, + threshold?: number, + scalingFactor?: number +) => TimeFormatter; // threshold defines the value from which upwards there should be no decimal places. function getUnitLabelAndConvertedValue( @@ -150,10 +154,15 @@ function getDurationUnitKey(max: number, threshold = 10): DurationTimeUnit { // memoizer with a custom resolver to consider both arguments max/threshold. // by default lodash's memoize only considers the first argument. export const getDurationFormatter: TimeFormatterBuilder = memoize( - (max: number, threshold: number = 10) => { + (max: number, threshold: number = 10, scalingFactor: number = 1) => { const unit = getDurationUnitKey(max, threshold); return (value: Maybe, { defaultValue }: FormatterOptions = {}) => { - return convertTo({ unit, microseconds: value, defaultValue, threshold }); + return convertTo({ + unit, + microseconds: isFiniteNumber(value) ? value * scalingFactor : value, + defaultValue, + threshold, + }); }; }, (max, threshold) => `${max}_${threshold}` diff --git a/x-pack/plugins/apm/public/assistant_functions/get_apm_service_summary.ts b/x-pack/plugins/apm/public/assistant_functions/get_apm_service_summary.ts index fbbd35524d43d..189633ec95975 100644 --- a/x-pack/plugins/apm/public/assistant_functions/get_apm_service_summary.ts +++ b/x-pack/plugins/apm/public/assistant_functions/get_apm_service_summary.ts @@ -8,6 +8,7 @@ import { i18n } from '@kbn/i18n'; import type { RegisterFunctionDefinition } from '@kbn/observability-ai-assistant-plugin/common/types'; import { callApmApi } from '../services/rest/create_call_apm_api'; +import { NON_EMPTY_STRING } from '../utils/non_empty_string_ref'; export function registerGetApmServiceSummaryFunction({ registerFunction, @@ -35,20 +36,20 @@ alerts and anomalies.`, type: 'object', properties: { 'service.name': { - type: 'string', + ...NON_EMPTY_STRING, description: 'The name of the service that should be summarized.', }, 'service.environment': { - type: 'string', + ...NON_EMPTY_STRING, description: 'The environment that the service is running in', }, start: { - type: 'string', + ...NON_EMPTY_STRING, description: 'The start of the time range, in Elasticsearch date math, like `now`.', }, end: { - type: 'string', + ...NON_EMPTY_STRING, description: 'The end of the time range, in Elasticsearch date math, like `now-24h`.', }, diff --git a/x-pack/plugins/apm/public/assistant_functions/get_apm_services_list.ts b/x-pack/plugins/apm/public/assistant_functions/get_apm_services_list.ts new file mode 100644 index 0000000000000..fdbcfb94da650 --- /dev/null +++ b/x-pack/plugins/apm/public/assistant_functions/get_apm_services_list.ts @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import type { RegisterFunctionDefinition } from '@kbn/observability-ai-assistant-plugin/common/types'; +import { callApmApi } from '../services/rest/create_call_apm_api'; +import { NON_EMPTY_STRING } from '../utils/non_empty_string_ref'; + +export function registerGetApmServicesListFunction({ + registerFunction, +}: { + registerFunction: RegisterFunctionDefinition; +}) { + registerFunction( + { + name: 'get_apm_services_list', + contexts: ['apm'], + description: `Gets a list of services`, + descriptionForUser: i18n.translate( + 'xpack.apm.observabilityAiAssistant.functions.registerGetApmServicesList.descriptionForUser', + { + defaultMessage: `Gets the list of monitored services, their health status, and alerts.`, + } + ), + parameters: { + type: 'object', + properties: { + 'service.environment': { + ...NON_EMPTY_STRING, + description: + 'Optionally filter the services by the environments that they are running in', + }, + start: { + ...NON_EMPTY_STRING, + description: + 'The start of the time range, in Elasticsearch date math, like `now`.', + }, + end: { + ...NON_EMPTY_STRING, + description: + 'The end of the time range, in Elasticsearch date math, like `now-24h`.', + }, + }, + required: ['start', 'end'], + } as const, + }, + async ({ arguments: args }, signal) => { + return callApmApi('GET /internal/apm/assistant/get_services_list', { + signal, + params: { + query: args, + }, + }); + } + ); +} diff --git a/x-pack/plugins/apm/public/assistant_functions/get_apm_timeseries.tsx b/x-pack/plugins/apm/public/assistant_functions/get_apm_timeseries.tsx index ff215e646148f..9bdfd4b4789d8 100644 --- a/x-pack/plugins/apm/public/assistant_functions/get_apm_timeseries.tsx +++ b/x-pack/plugins/apm/public/assistant_functions/get_apm_timeseries.tsx @@ -31,6 +31,7 @@ import { getMaxY, getResponseTimeTickFormatter, } from '../components/shared/charts/transaction_charts/helper'; +import { NON_EMPTY_STRING } from '../utils/non_empty_string_ref'; export function registerGetApmTimeseriesFunction({ registerFunction, @@ -47,7 +48,7 @@ export function registerGetApmTimeseriesFunction({ defaultMessage: `Display different APM metrics, like throughput, failure rate, or latency, for any service or all services, or any or all of its dependencies, both as a timeseries and as a single statistic. Additionally, the function will return any changes, such as spikes, step and trend changes, or dips. You can also use it to compare data by requesting two different time ranges, or for instance two different service versions`, } ), - description: `Display different APM metrics, like throughput, failure rate, or latency, for any service or all services, or any or all of its dependencies, both as a timeseries and as a single statistic. Additionally, the function will return any changes, such as spikes, step and trend changes, or dips. You can also use it to compare data by requesting two different time ranges, or for instance two different service versions. In KQL, escaping happens with double quotes, not single quotes. Some characters that need escaping are: ':()\\\/\". Always put a field value in double quotes. Best: service.name:\"opbeans-go\". Wrong: service.name:opbeans-go. This is very important!`, + description: `Visualise and analyse different APM metrics, like throughput, failure rate, or latency, for any service or all services, or any or all of its dependencies, both as a timeseries and as a single statistic. A visualisation will be displayed above your reply - DO NOT attempt to display or generate an image yourself, or any other placeholder. Additionally, the function will return any changes, such as spikes, step and trend changes, or dips. You can also use it to compare data by requesting two different time ranges, or for instance two different service versions.`, parameters: { type: 'object', properties: { @@ -135,11 +136,11 @@ export function registerGetApmTimeseriesFunction({ ], }, 'service.name': { - type: 'string', + ...NON_EMPTY_STRING, description: 'The name of the service', }, 'service.environment': { - type: 'string', + ...NON_EMPTY_STRING, description: 'The environment that the service is running in.', }, @@ -201,7 +202,7 @@ export function registerGetApmTimeseriesFunction({ const groupId = groupSeries[0].group; const maxY = getMaxY(groupSeries); - const latencyFormatter = getDurationFormatter(maxY); + const latencyFormatter = getDurationFormatter(maxY, 10, 1000); let yLabelFormat: (value: number) => string; @@ -230,6 +231,8 @@ export function registerGetApmTimeseriesFunction({ groupSeries.map((series): TimeSeries => { let chartType: ChartType; + const data = series.data; + switch (series.stat.timeseries.name) { case 'transaction_throughput': case 'exit_span_throughput': @@ -270,7 +273,7 @@ export function registerGetApmTimeseriesFunction({ title: series.id, type: 'line', color: getTimeSeriesColor(chartType!).currentPeriodColor, - data: series.data, + data, }; }); diff --git a/x-pack/plugins/apm/public/assistant_functions/index.ts b/x-pack/plugins/apm/public/assistant_functions/index.ts index f83e9fa6fc575..128091cf2472d 100644 --- a/x-pack/plugins/apm/public/assistant_functions/index.ts +++ b/x-pack/plugins/apm/public/assistant_functions/index.ts @@ -18,6 +18,7 @@ import { import { registerGetApmCorrelationsFunction } from './get_apm_correlations'; import { registerGetApmDownstreamDependenciesFunction } from './get_apm_downstream_dependencies'; import { registerGetApmErrorDocumentFunction } from './get_apm_error_document'; +import { registerGetApmServicesListFunction } from './get_apm_services_list'; import { registerGetApmServiceSummaryFunction } from './get_apm_service_summary'; import { registerGetApmTimeseriesFunction } from './get_apm_timeseries'; @@ -64,82 +65,92 @@ export async function registerAssistantFunctions({ registerFunction, }); + registerGetApmServicesListFunction({ + registerFunction, + }); + registerContext({ name: 'apm', description: ` -There are four important data types in Elastic APM. Each of them have the -following fields: -- service.name: the name of the service -- service.node.name: the id of the service instance (often the hostname) -- service.environment: the environment (often production, development) -- agent.name: the name of the agent (go, java, etc) - -The four data types are transactions, exit spans, error events, and application -metrics. - -Transactions have three metrics: throughput, failure rate, and latency. The -fields are: - -- transaction.type: often request or page-load (the main transaction types), -but can also be worker, or route-change. -- transaction.name: The name of the transaction group, often something like -'GET /api/product/:productId' -- transaction.result: The result. Used to capture HTTP response codes -(2xx,3xx,4xx,5xx) for request transactions. -- event.outcome: whether the transaction was succesful or not. success, -failure, or unknown. - -Exit spans have three metrics: throughput, failure rate and latency. The fields -are: -- span.type: db, external -- span.subtype: the type of database (redis, postgres) or protocol (http, grpc) -- span.destination.service.resource: the address of the destination of the call -- event.outcome: whether the transaction was succesful or not. success, -failure, or unknown. - -Error events have one metric, error event rate. The fields are: -- error.grouping_name: a human readable keyword that identifies the error group - -For transaction metrics we also collect anomalies. These are scored 0 (low) to -100 (critical). - -For root cause analysis, locate a change point in the relevant metrics for a -service or downstream dependency. You can locate a change point by using a -sliding window, e.g. start with a small time range, like 30m, and make it -bigger until you identify a change point. It's very important to identify a -change point. If you don't have a change point, ask the user for next steps. -You can also use an anomaly or a deployment as a change point. Then, compare -data before the change with data after the change. You can either use the -groupBy parameter in get_apm_chart to get the most occuring values in a certain -data set, or you can use correlations to see for which field and value the -frequency has changed when comparing the foreground set to the background set. -This is useful when comparing data from before the change point with after the -change point. For instance, you might see a specific error pop up more often -after the change point. - -When comparing anomalies and changes in timeseries, first, zoom in to a smaller -time window, at least 30 minutes before and 30 minutes after the change -occured. E.g., if the anomaly occured at 2023-07-05T08:15:00.000Z, request a -time window that starts at 2023-07-05T07:45:00.000Z and ends at -2023-07-05T08:45:00.000Z. When comparing changes in different timeseries and -anomalies to determine a correlation, make sure to compare the timestamps. If -in doubt, rate the likelihood of them being related, given the time difference, -between 1 and 10. If below 5, assume it's not related. Mention this likelihood -(and the time difference) to the user. - -Your goal is to help the user determine the root cause of an issue quickly and -transparently. If you see a change or -anomaly in a metric for a service, try to find similar changes in the metrics -for the traffic to its downstream dependencies, by comparing transaction -metrics to span metrics. To inspect the traffic from one service to a -downstream dependency, first get the downstream dependencies for a service, -then get the span metrics from that service (\`service.name\`) to its -downstream dependency (\`span.destination.service.resource\`). For instance, -for an anomaly in throughput, first inspect \`transaction_throughput\` for -\`service.name\`. Then, inspect \`exit_span_throughput\` for its downstream -dependencies, by grouping by \`span.destination.service.resource\`. Repeat this -process over the next service its downstream dependencies until you identify a -root cause. If you can not find any similar changes, use correlations or -grouping to find attributes that could be causes for the change.`, + When analyzing APM data, prefer the APM specific functions over the generic Lens, + Elasticsearch or Kibana ones, unless those are explicitly requested by the user. + + When requesting metrics for a service, make sure you also know what environment + it is running in. Metrics aggregated over multiple environments are useless. + + There are four important data types in Elastic APM. Each of them have the + following fields: + - service.name: the name of the service + - service.node.name: the id of the service instance (often the hostname) + - service.environment: the environment (often production, development) + - agent.name: the name of the agent (go, java, etc) + + The four data types are transactions, exit spans, error events, and application + metrics. + + Transactions have three metrics: throughput, failure rate, and latency. The + fields are: + + - transaction.type: often request or page-load (the main transaction types), + but can also be worker, or route-change. + - transaction.name: The name of the transaction group, often something like + 'GET /api/product/:productId' + - transaction.result: The result. Used to capture HTTP response codes + (2xx,3xx,4xx,5xx) for request transactions. + - event.outcome: whether the transaction was succesful or not. success, + failure, or unknown. + + Exit spans have three metrics: throughput, failure rate and latency. The fields + are: + - span.type: db, external + - span.subtype: the type of database (redis, postgres) or protocol (http, grpc) + - span.destination.service.resource: the address of the destination of the call + - event.outcome: whether the transaction was succesful or not. success, + failure, or unknown. + + Error events have one metric, error event rate. The fields are: + - error.grouping_name: a human readable keyword that identifies the error group + + For transaction metrics we also collect anomalies. These are scored 0 (low) to + 100 (critical). + + For root cause analysis, locate a change point in the relevant metrics for a + service or downstream dependency. You can locate a change point by using a + sliding window, e.g. start with a small time range, like 30m, and make it + bigger until you identify a change point. It's very important to identify a + change point. If you don't have a change point, ask the user for next steps. + You can also use an anomaly or a deployment as a change point. Then, compare + data before the change with data after the change. You can either use the + groupBy parameter in get_apm_chart to get the most occuring values in a certain + data set, or you can use correlations to see for which field and value the + frequency has changed when comparing the foreground set to the background set. + This is useful when comparing data from before the change point with after the + change point. For instance, you might see a specific error pop up more often + after the change point. + + When comparing anomalies and changes in timeseries, first, zoom in to a smaller + time window, at least 30 minutes before and 30 minutes after the change + occured. E.g., if the anomaly occured at 2023-07-05T08:15:00.000Z, request a + time window that starts at 2023-07-05T07:45:00.000Z and ends at + 2023-07-05T08:45:00.000Z. When comparing changes in different timeseries and + anomalies to determine a correlation, make sure to compare the timestamps. If + in doubt, rate the likelihood of them being related, given the time difference, + between 1 and 10. If below 5, assume it's not related. Mention this likelihood + (and the time difference) to the user. + + Your goal is to help the user determine the root cause of an issue quickly and + transparently. If you see a change or + anomaly in a metric for a service, try to find similar changes in the metrics + for the traffic to its downstream dependencies, by comparing transaction + metrics to span metrics. To inspect the traffic from one service to a + downstream dependency, first get the downstream dependencies for a service, + then get the span metrics from that service (\`service.name\`) to its + downstream dependency (\`span.destination.service.resource\`). For instance, + for an anomaly in throughput, first inspect \`transaction_throughput\` for + \`service.name\`. Then, inspect \`exit_span_throughput\` for its downstream + dependencies, by grouping by \`span.destination.service.resource\`. Repeat this + process over the next service its downstream dependencies until you identify a + root cause. If you can not find any similar changes, use correlations or + grouping to find attributes that could be causes for the change.`, }); } diff --git a/x-pack/plugins/apm/public/utils/non_empty_string_ref.ts b/x-pack/plugins/apm/public/utils/non_empty_string_ref.ts new file mode 100644 index 0000000000000..94cac08fa2198 --- /dev/null +++ b/x-pack/plugins/apm/public/utils/non_empty_string_ref.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const NON_EMPTY_STRING = { + type: 'string' as const, + minLength: 1, +}; diff --git a/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_timeseries/get_error_event_rate.ts b/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_timeseries/get_error_event_rate.ts index 85debffb41742..9022d4fc87e4b 100644 --- a/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_timeseries/get_error_event_rate.ts +++ b/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_timeseries/get_error_event_rate.ts @@ -6,7 +6,6 @@ */ import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; -import { rangeQuery } from '@kbn/observability-plugin/server'; import { ApmDocumentType } from '../../../../common/document_type'; import { RollupInterval } from '../../../../common/rollup'; import type { APMEventClient } from '../../../lib/helpers/create_es_client/create_apm_event_client'; @@ -40,7 +39,7 @@ export async function getErrorEventRate({ documentType: ApmDocumentType.ErrorEvent, rollupInterval: RollupInterval.None, intervalString, - filter: filter.concat(...rangeQuery(start, end)), + filter, aggs: { value: { bucket_script: { diff --git a/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_timeseries/get_exit_span_failure_rate.ts b/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_timeseries/get_exit_span_failure_rate.ts index 655d03dd87448..35ca607e9e338 100644 --- a/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_timeseries/get_exit_span_failure_rate.ts +++ b/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_timeseries/get_exit_span_failure_rate.ts @@ -6,7 +6,7 @@ */ import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; -import { rangeQuery, termQuery } from '@kbn/observability-plugin/server'; +import { termQuery } from '@kbn/observability-plugin/server'; import { ApmDocumentType } from '../../../../common/document_type'; import { EVENT_OUTCOME, @@ -45,7 +45,6 @@ export async function getExitSpanFailureRate({ rollupInterval: RollupInterval.OneMinute, intervalString, filter: filter.concat( - ...rangeQuery(start, end), ...termQuery( SPAN_DESTINATION_SERVICE_RESOURCE, spanDestinationServiceResource diff --git a/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_timeseries/get_exit_span_latency.ts b/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_timeseries/get_exit_span_latency.ts index 1d5bcd04cd35c..ad1f8cb8791be 100644 --- a/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_timeseries/get_exit_span_latency.ts +++ b/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_timeseries/get_exit_span_latency.ts @@ -6,7 +6,7 @@ */ import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; -import { rangeQuery, termQuery } from '@kbn/observability-plugin/server'; +import { termQuery } from '@kbn/observability-plugin/server'; import { ApmDocumentType } from '../../../../common/document_type'; import { SPAN_DESTINATION_SERVICE_RESOURCE, @@ -39,12 +39,11 @@ export async function getExitSpanLatency({ start, end, operationName: 'assistant_get_exit_span_latency', - unit: 'rpm', + unit: 'ms', documentType: ApmDocumentType.ServiceDestinationMetric, rollupInterval: RollupInterval.OneMinute, intervalString, filter: filter.concat( - ...rangeQuery(start, end), ...termQuery( SPAN_DESTINATION_SERVICE_RESOURCE, spanDestinationServiceResource diff --git a/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_timeseries/get_exit_span_throughput.ts b/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_timeseries/get_exit_span_throughput.ts index dd49c4c7d79f4..ab1be9d2c20dc 100644 --- a/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_timeseries/get_exit_span_throughput.ts +++ b/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_timeseries/get_exit_span_throughput.ts @@ -6,7 +6,7 @@ */ import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; -import { rangeQuery, termQuery } from '@kbn/observability-plugin/server'; +import { termQuery } from '@kbn/observability-plugin/server'; import { ApmDocumentType } from '../../../../common/document_type'; import { SPAN_DESTINATION_SERVICE_RESOURCE } from '../../../../common/es_fields/apm'; import { RollupInterval } from '../../../../common/rollup'; @@ -44,7 +44,6 @@ export async function getExitSpanThroughput({ rollupInterval: RollupInterval.OneMinute, intervalString, filter: filter.concat( - ...rangeQuery(start, end), ...termQuery( SPAN_DESTINATION_SERVICE_RESOURCE, spanDestinationServiceResource @@ -73,7 +72,7 @@ export async function getExitSpanThroughput({ ...fetchedSerie, value: fetchedSerie.value !== null - ? fetchedSerie.value / rangeInMinutes + ? (fetchedSerie.value * bucketSizeInMinutes) / rangeInMinutes : null, data: fetchedSerie.data.map((bucket) => { return { diff --git a/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_timeseries/get_transaction_failure_rate.ts b/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_timeseries/get_transaction_failure_rate.ts index b4b0704ce6ea9..8e44eac64e3e9 100644 --- a/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_timeseries/get_transaction_failure_rate.ts +++ b/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_timeseries/get_transaction_failure_rate.ts @@ -6,7 +6,7 @@ */ import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; -import { rangeQuery, termQuery } from '@kbn/observability-plugin/server'; +import { termQuery } from '@kbn/observability-plugin/server'; import { ApmDocumentType } from '../../../../common/document_type'; import { TRANSACTION_TYPE } from '../../../../common/es_fields/apm'; import { RollupInterval } from '../../../../common/rollup'; @@ -40,10 +40,7 @@ export async function getTransactionFailureRate({ documentType: ApmDocumentType.TransactionMetric, rollupInterval: RollupInterval.OneMinute, intervalString, - filter: filter.concat( - ...rangeQuery(start, end), - ...termQuery(TRANSACTION_TYPE, transactionType) - ), + filter: filter.concat(...termQuery(TRANSACTION_TYPE, transactionType)), groupBy: 'transaction.type', aggs: { ...getOutcomeAggregation(ApmDocumentType.TransactionMetric), diff --git a/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_timeseries/get_transaction_latency.ts b/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_timeseries/get_transaction_latency.ts index 8cbb6dd74d2d9..f51378c647fd4 100644 --- a/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_timeseries/get_transaction_latency.ts +++ b/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_timeseries/get_transaction_latency.ts @@ -6,7 +6,7 @@ */ import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; -import { rangeQuery, termQuery } from '@kbn/observability-plugin/server'; +import { termQuery } from '@kbn/observability-plugin/server'; import { ApmDocumentType } from '../../../../common/document_type'; import { TRANSACTION_DURATION_HISTOGRAM, @@ -41,15 +41,12 @@ export async function getTransactionLatency({ apmEventClient, start, end, - operationName: 'assistant_get_transaction_latencyu', - unit: 'rpm', + operationName: 'assistant_get_transaction_latency', + unit: 'ms', documentType: ApmDocumentType.TransactionMetric, rollupInterval: RollupInterval.OneMinute, intervalString, - filter: filter.concat( - ...rangeQuery(start, end), - ...termQuery(TRANSACTION_TYPE, transactionType) - ), + filter: filter.concat(...termQuery(TRANSACTION_TYPE, transactionType)), groupBy: 'transaction.type', aggs: { ...getLatencyAggregation( diff --git a/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_timeseries/get_transaction_throughput.ts b/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_timeseries/get_transaction_throughput.ts index 919f2b63e5165..891ebd8b6a356 100644 --- a/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_timeseries/get_transaction_throughput.ts +++ b/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_timeseries/get_transaction_throughput.ts @@ -6,7 +6,7 @@ */ import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; -import { rangeQuery, termQuery } from '@kbn/observability-plugin/server'; +import { termQuery } from '@kbn/observability-plugin/server'; import { ApmDocumentType } from '../../../../common/document_type'; import { TRANSACTION_TYPE } from '../../../../common/es_fields/apm'; import { RollupInterval } from '../../../../common/rollup'; @@ -43,10 +43,7 @@ export async function getTransactionThroughput({ documentType: ApmDocumentType.TransactionMetric, rollupInterval: RollupInterval.OneMinute, intervalString, - filter: filter.concat( - ...rangeQuery(start, end), - ...termQuery(TRANSACTION_TYPE, transactionType) - ), + filter: filter.concat(...termQuery(TRANSACTION_TYPE, transactionType)), groupBy: 'transaction.type', aggs: { value: { @@ -70,7 +67,7 @@ export async function getTransactionThroughput({ ...fetchedSerie, value: fetchedSerie.value !== null - ? fetchedSerie.value / rangeInMinutes + ? (fetchedSerie.value * bucketSizeInMinutes) / rangeInMinutes : null, data: fetchedSerie.data.map((bucket) => { return { diff --git a/x-pack/plugins/apm/server/routes/assistant_functions/route.ts b/x-pack/plugins/apm/server/routes/assistant_functions/route.ts index bd2d32c90907e..1d02003c5e46c 100644 --- a/x-pack/plugins/apm/server/routes/assistant_functions/route.ts +++ b/x-pack/plugins/apm/server/routes/assistant_functions/route.ts @@ -4,14 +4,22 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import datemath from '@elastic/datemath'; import { ElasticsearchClient } from '@kbn/core/server'; import * as t from 'io-ts'; import { omit } from 'lodash'; +import { ApmDocumentType } from '../../../common/document_type'; +import { ENVIRONMENT_ALL } from '../../../common/environment_filter_values'; +import { RollupInterval } from '../../../common/rollup'; +import { ServiceHealthStatus } from '../../../common/service_health_status'; import type { APMError } from '../../../typings/es_schemas/ui/apm_error'; import { getApmAlertsClient } from '../../lib/helpers/get_apm_alerts_client'; import { getApmEventClient } from '../../lib/helpers/get_apm_event_client'; import { getMlClient } from '../../lib/helpers/get_ml_client'; +import { getRandomSampler } from '../../lib/helpers/get_random_sampler'; import { createApmServerRoute } from '../apm_routes/create_apm_server_route'; +import { environmentRt } from '../default_api_types'; +import { getServicesItems } from '../services/get_services/get_services_items'; import { CorrelationValue, correlationValuesRouteRt, @@ -184,10 +192,89 @@ const getApmErrorDocRoute = createApmServerRoute({ }, }); +interface ApmServicesListItem { + 'service.name': string; + 'agent.name'?: string; + 'transaction.type'?: string; + alertsCount: number; + healthStatus: ServiceHealthStatus; + 'service.environment'?: string[]; +} + +type ApmServicesListContent = ApmServicesListItem[]; + +const getApmServicesListRoute = createApmServerRoute({ + endpoint: 'GET /internal/apm/assistant/get_services_list', + params: t.type({ + query: t.intersection([ + t.type({ + start: t.string, + end: t.string, + }), + t.partial({ + 'service.environment': environmentRt.props.environment, + }), + ]), + }), + options: { + tags: ['access:apm'], + }, + handler: async (resources): Promise<{ content: ApmServicesListContent }> => { + const { params } = resources; + const { query } = params; + + const [apmEventClient, apmAlertsClient, mlClient, randomSampler] = + await Promise.all([ + getApmEventClient(resources), + getApmAlertsClient(resources), + getMlClient(resources), + getRandomSampler({ + security: resources.plugins.security, + probability: 1, + request: resources.request, + }), + ]); + + const start = datemath.parse(query.start)?.valueOf()!; + const end = datemath.parse(query.end)?.valueOf()!; + + const serviceItems = await getServicesItems({ + apmAlertsClient, + apmEventClient, + documentType: ApmDocumentType.TransactionMetric, + start, + end, + environment: query['service.environment'] ?? ENVIRONMENT_ALL.value, + kuery: '', + logger: resources.logger, + randomSampler, + rollupInterval: RollupInterval.OneMinute, + serviceGroup: null, + mlClient, + }); + + const mappedItems = serviceItems.items.map((item): ApmServicesListItem => { + return { + 'service.name': item.serviceName, + 'agent.name': item.agentName, + alertsCount: item.alertsCount ?? 0, + healthStatus: item.healthStatus ?? ServiceHealthStatus.unknown, + 'service.environment': item.environments, + 'transaction.type': item.transactionType, + }; + }); + + return { + content: mappedItems, + }; + }, +}); + export const assistantRouteRepository = { ...getApmTimeSeriesRoute, ...getApmServiceSummaryRoute, ...getApmErrorDocRoute, ...getApmCorrelationValuesRoute, ...getDownstreamDependenciesRoute, + ...getApmServicesListRoute, }; diff --git a/x-pack/plugins/apm_data_access/server/index.ts b/x-pack/plugins/apm_data_access/server/index.ts index 4d7ae2b1eb6c3..4ef9a47937733 100644 --- a/x-pack/plugins/apm_data_access/server/index.ts +++ b/x-pack/plugins/apm_data_access/server/index.ts @@ -16,6 +16,7 @@ const configSchema = schema.object({ error: schema.string({ defaultValue: 'logs-apm*,apm-*' }), metric: schema.string({ defaultValue: 'metrics-apm*,apm-*' }), onboarding: schema.string({ defaultValue: 'apm-*' }), // Unused: to be deleted + sourcemap: schema.string({ defaultValue: 'apm-*' }), // Unused: to be deleted }), }); @@ -23,7 +24,11 @@ const configSchema = schema.object({ export const config: PluginConfigDescriptor = { deprecations: ({ renameFromRoot, unused, deprecate }) => [ // deprecations - unused('indices.sourcemap', { level: 'warning' }), + deprecate('indices.sourcemap', 'a future version', { + level: 'warning', + message: `Configuring "xpack.apm.indices.sourcemap" is deprecated and will be removed in a future version. Please remove this setting.`, + }), + deprecate('indices.onboarding', 'a future version', { level: 'warning', message: `Configuring "xpack.apm.indices.onboarding" is deprecated and will be removed in a future version. Please remove this setting.`, diff --git a/x-pack/plugins/cloud_security_posture/common/constants.ts b/x-pack/plugins/cloud_security_posture/common/constants.ts index 5ba3648638e61..fa5737f7f1531 100644 --- a/x-pack/plugins/cloud_security_posture/common/constants.ts +++ b/x-pack/plugins/cloud_security_posture/common/constants.ts @@ -5,7 +5,12 @@ * 2.0. */ -import { PostureTypes, VulnSeverity, AwsCredentialsTypeFieldMap } from './types'; +import { + PostureTypes, + VulnSeverity, + AwsCredentialsTypeFieldMap, + GcpCredentialsTypeFieldMap, +} from './types'; export const STATUS_ROUTE_PATH = '/internal/cloud_security_posture/status'; export const STATUS_API_CURRENT_VERSION = '1'; @@ -143,3 +148,8 @@ export const SETUP_ACCESS_CLOUD_SHELL = 'google_cloud_shell'; export const SETUP_ACCESS_MANUAL = 'manual'; export const DETECTION_ENGINE_ALERTS_INDEX_DEFAULT = '.alerts-security.alerts-default'; + +export const GCP_CREDENTIALS_TYPE_TO_FIELDS_MAP: GcpCredentialsTypeFieldMap = { + 'credentials-file': ['gcp.credentials.file'], + 'credentials-json': ['gcp.credentials.json'], +}; diff --git a/x-pack/plugins/cloud_security_posture/common/types.ts b/x-pack/plugins/cloud_security_posture/common/types.ts index 956493de4c573..f2b6c00399915 100644 --- a/x-pack/plugins/cloud_security_posture/common/types.ts +++ b/x-pack/plugins/cloud_security_posture/common/types.ts @@ -24,6 +24,12 @@ export type AwsCredentialsTypeFieldMap = { [key in AwsCredentialsType]: string[]; }; +export type GcpCredentialsType = 'credentials-file' | 'credentials-json'; + +export type GcpCredentialsTypeFieldMap = { + [key in GcpCredentialsType]: string[]; +}; + export type Evaluation = 'passed' | 'failed' | 'NA'; export type PostureTypes = 'cspm' | 'kspm' | 'vuln_mgmt' | 'all'; diff --git a/x-pack/plugins/cloud_security_posture/common/utils/get_safe_vulnerabilities_query_filter.ts b/x-pack/plugins/cloud_security_posture/common/utils/get_safe_vulnerabilities_query_filter.ts deleted file mode 100644 index 899bc5177458e..0000000000000 --- a/x-pack/plugins/cloud_security_posture/common/utils/get_safe_vulnerabilities_query_filter.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; - -export const getSafeVulnerabilitiesQueryFilter = (query?: QueryDslQueryContainer) => ({ - ...query, - bool: { - ...query?.bool, - filter: [...((query?.bool?.filter as []) || []), { exists: { field: 'resource.id' } }], - }, -}); diff --git a/x-pack/plugins/cloud_security_posture/common/utils/helpers.test.ts b/x-pack/plugins/cloud_security_posture/common/utils/helpers.test.ts index 479cc2f62c36a..7e7d25a5b28bd 100644 --- a/x-pack/plugins/cloud_security_posture/common/utils/helpers.test.ts +++ b/x-pack/plugins/cloud_security_posture/common/utils/helpers.test.ts @@ -185,5 +185,109 @@ describe('test helper methods', () => { shared_credential_file: { value: undefined, type: 'text' }, }); }); + + it('when aws credential type is undefined, return unchanged policy', () => { + const mockPackagePolicy = createPackagePolicyMock(); + mockPackagePolicy.inputs = [ + { + type: 'cloudbeat/cis_eks', + enabled: true, + streams: [ + { + id: 'findings', + enabled: true, + data_stream: { + dataset: 'cloud_security_posture.findings', + type: 'logs', + }, + vars: { + 'aws.credentials.type': { value: undefined }, + access_key_id: { value: 'used', type: 'text' }, + credential_profile_name: { value: 'unused', type: 'text' }, + role_arn: { value: 'unused' }, + secret_access_key: { value: 'used', type: 'text' }, + session_token: { value: 'unused', type: 'text' }, + shared_credential_file: { value: 'unused', type: 'text' }, + }, + }, + ], + }, + ]; + + const cleanedPackage = cleanupCredentials(mockPackagePolicy); + expect(cleanedPackage.inputs[0].streams[0].vars).toEqual({ + 'aws.credentials.type': { value: undefined }, + access_key_id: { value: 'used', type: 'text' }, + credential_profile_name: { value: 'unused', type: 'text' }, + role_arn: { value: 'unused' }, + secret_access_key: { value: 'used', type: 'text' }, + session_token: { value: 'unused', type: 'text' }, + shared_credential_file: { value: 'unused', type: 'text' }, + }); + }); + + it('cleans unused gcp credential methods, when using credentials-file method ', () => { + const mockPackagePolicy = createPackagePolicyMock(); + mockPackagePolicy.inputs = [ + { + type: 'cloudbeat/cis_gcp', + enabled: true, + streams: [ + { + id: 'findings', + enabled: true, + data_stream: { + dataset: 'cloud_security_posture.findings', + type: 'logs', + }, + vars: { + 'gcp.credentials.type': { value: 'credentials-file' }, + 'gcp.credentials.file': { value: 'used' }, + 'gcp.credentials.json': { value: 'unused' }, + }, + }, + ], + }, + ]; + + const cleanedPackage = cleanupCredentials(mockPackagePolicy); + expect(cleanedPackage.inputs[0].streams[0].vars).toEqual({ + 'gcp.credentials.type': { value: 'credentials-file' }, + 'gcp.credentials.file': { value: 'used' }, + 'gcp.credentials.json': { value: undefined }, + }); + }); + + it('when gcp credential type is undefined, return unchanged policy', () => { + const mockPackagePolicy = createPackagePolicyMock(); + mockPackagePolicy.inputs = [ + { + type: 'cloudbeat/cis_gcp', + enabled: true, + streams: [ + { + id: 'findings', + enabled: true, + data_stream: { + dataset: 'cloud_security_posture.findings', + type: 'logs', + }, + vars: { + 'gcp.credentials.type': { value: undefined }, + 'gcp.credentials.file': { value: 'used' }, + 'gcp.credentials.json': { value: 'unused' }, + }, + }, + ], + }, + ]; + + const cleanedPackage = cleanupCredentials(mockPackagePolicy); + expect(cleanedPackage.inputs[0].streams[0].vars).toEqual({ + 'gcp.credentials.type': { value: undefined }, + 'gcp.credentials.file': { value: 'used' }, + 'gcp.credentials.json': { value: 'unused' }, + }); + }); }); }); diff --git a/x-pack/plugins/cloud_security_posture/common/utils/helpers.ts b/x-pack/plugins/cloud_security_posture/common/utils/helpers.ts index f50a5ef47bb59..9cf31c944d27a 100644 --- a/x-pack/plugins/cloud_security_posture/common/utils/helpers.ts +++ b/x-pack/plugins/cloud_security_posture/common/utils/helpers.ts @@ -19,8 +19,15 @@ import { CLOUDBEAT_VANILLA, CSP_RULE_TEMPLATE_SAVED_OBJECT_TYPE, AWS_CREDENTIALS_TYPE_TO_FIELDS_MAP, + GCP_CREDENTIALS_TYPE_TO_FIELDS_MAP, } from '../constants'; -import type { BenchmarkId, Score, BaseCspSetupStatus, AwsCredentialsType } from '../types'; +import type { + BenchmarkId, + Score, + BaseCspSetupStatus, + AwsCredentialsType, + GcpCredentialsType, +} from '../types'; /** * @example @@ -103,12 +110,21 @@ export const getStatusForIndexName = (indexName: string, status?: BaseCspSetupSt export const cleanupCredentials = (packagePolicy: NewPackagePolicy | UpdatePackagePolicy) => { const enabledInput = packagePolicy.inputs.find((i) => i.enabled); - const credentialType: AwsCredentialsType | undefined = + const awsCredentialType: AwsCredentialsType | undefined = enabledInput?.streams?.[0].vars?.['aws.credentials.type']?.value; - - if (credentialType) { - const credsToKeep = AWS_CREDENTIALS_TYPE_TO_FIELDS_MAP[credentialType]; - const credFields = Object.values(AWS_CREDENTIALS_TYPE_TO_FIELDS_MAP).flat(); + const gcpCredentialType: GcpCredentialsType | undefined = + enabledInput?.streams?.[0].vars?.['gcp.credentials.type']?.value; + + if (awsCredentialType || gcpCredentialType) { + let credsToKeep: string[] = [' ']; + let credFields: string[] = [' ']; + if (awsCredentialType) { + credsToKeep = AWS_CREDENTIALS_TYPE_TO_FIELDS_MAP[awsCredentialType]; + credFields = Object.values(AWS_CREDENTIALS_TYPE_TO_FIELDS_MAP).flat(); + } else if (gcpCredentialType) { + credsToKeep = GCP_CREDENTIALS_TYPE_TO_FIELDS_MAP[gcpCredentialType]; + credFields = Object.values(GCP_CREDENTIALS_TYPE_TO_FIELDS_MAP).flat(); + } if (credsToKeep) { // we need to return a copy of the policy with the unused diff --git a/x-pack/plugins/cloud_security_posture/public/common/constants.ts b/x-pack/plugins/cloud_security_posture/public/common/constants.ts index 8f51456c009e4..4a5d5bfc7bd9a 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/constants.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/constants.ts @@ -214,7 +214,7 @@ export const cloudPostureIntegrations: CloudPostureIntegrations = { }, }; export const FINDINGS_DOCS_URL = 'https://ela.st/findings'; -export const MIN_VERSION_GCP_CIS = '1.5.0'; +export const MIN_VERSION_GCP_CIS = '1.5.2'; export const NO_FINDINGS_STATUS_REFRESH_INTERVAL_MS = 10000; diff --git a/x-pack/plugins/cloud_security_posture/public/common/hooks/use_cloud_posture_table/use_cloud_posture_table.ts b/x-pack/plugins/cloud_security_posture/public/common/hooks/use_cloud_posture_table/use_cloud_posture_table.ts index b26af81cdff43..2d42c2a8303d6 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/hooks/use_cloud_posture_table/use_cloud_posture_table.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/hooks/use_cloud_posture_table/use_cloud_posture_table.ts @@ -4,12 +4,39 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { useCallback } from 'react'; +import { Dispatch, SetStateAction, useCallback } from 'react'; import { type DataView } from '@kbn/data-views-plugin/common'; +import { BoolQuery } from '@kbn/es-query'; +import { CriteriaWithPagination } from '@elastic/eui'; import { useUrlQuery } from '../use_url_query'; import { usePageSize } from '../use_page_size'; import { getDefaultQuery, useBaseEsQuery, usePersistedQuery } from './utils'; +interface QuerySort { + direction: string; + id: string; +} + +export interface CloudPostureTableResult { + setUrlQuery: (query: any) => void; + // TODO: remove any, this sorting is used for both EuiGrid and EuiTable which uses different types of sorts + sort: any; + filters: any[]; + query?: { bool: BoolQuery }; + queryError?: Error; + pageIndex: number; + // TODO: remove any, urlQuery is an object with query fields but we also add custom fields to it, need to assert usages + urlQuery: any; + setTableOptions: (options: CriteriaWithPagination) => void; + handleUpdateQuery: (query: any) => void; + pageSize: number; + setPageSize: Dispatch>; + onChangeItemsPerPage: (newPageSize: number) => void; + onChangePage: (newPageIndex: number) => void; + onSort: (sort: QuerySort[]) => void; + onResetFilters: () => void; +} + /* Hook for managing common table state and methods for Cloud Posture */ @@ -21,7 +48,7 @@ export const useCloudPostureTable = ({ defaultQuery?: (params: any) => any; dataView: DataView; paginationLocalStorageKey: string; -}) => { +}): CloudPostureTableResult => { const getPersistedDefaultQuery = usePersistedQuery(defaultQuery); const { urlQuery, setUrlQuery } = useUrlQuery(getPersistedDefaultQuery); const { pageSize, setPageSize } = usePageSize(paginationLocalStorageKey); @@ -31,9 +58,10 @@ export const useCloudPostureTable = ({ setPageSize(newPageSize); setUrlQuery({ pageIndex: 0, + pageSize: newPageSize, }); }, - [setUrlQuery, setPageSize] + [setPageSize, setUrlQuery] ); const onResetFilters = useCallback(() => { diff --git a/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx b/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx index 490df0c3d9215..39b37c85aee39 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx @@ -33,8 +33,8 @@ export const AccountsEvaluatedWidget = ({ const cisAwsClusterAmount = filterClustersById(CIS_AWS).length; const cisGcpClusterAmount = filterClustersById(CIS_GCP).length; - const cisAwsBenchmarkName = filterClustersById(CIS_AWS)[0]?.meta.benchmark.name || ''; - const cisGcpBenchmarkName = filterClustersById(CIS_GCP)[0]?.meta.benchmark.name || ''; + const cisAwsBenchmarkName = 'Amazon Web Services (AWS)'; + const cisGcpBenchmarkName = 'Google Cloud Platform (GCP)'; return ( <> diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/aws_credentials_form.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/aws_credentials_form.tsx index 7fafe17113c84..dc2b46dd364fe 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/aws_credentials_form.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/aws_credentials_form.tsx @@ -235,7 +235,9 @@ export const AwsCredentialsForm = ({ size="m" options={getSetupFormatOptions()} idSelected={setupFormat} - onChange={onSetupFormatChange} + onChange={(idSelected: SetupFormat) => + idSelected !== setupFormat && onSetupFormatChange(idSelected) + } /> {setupFormat === 'cloud_formation' && ( diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credential_form.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credential_form.tsx index 88f6ff77fde21..74685000a98cc 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credential_form.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credential_form.tsx @@ -110,56 +110,58 @@ const GoogleCloudShellSetup = () => { ); }; -type GcpFields = Record; +const credentialOptionsList = [ + { + text: i18n.translate('xpack.csp.gcpIntegration.credentialsFileOption', { + defaultMessage: 'Credentials File', + }), + value: 'credentials-file', + }, + { + text: i18n.translate('xpack.csp.gcpIntegration.credentialsJsonOption', { + defaultMessage: 'Credentials JSON', + }), + value: 'credentials-json', + }, +]; + +type GcpFields = Record; interface GcpInputFields { fields: GcpFields; } export const gcpField: GcpInputFields = { fields: { - project_id: { + 'gcp.project_id': { label: i18n.translate('xpack.csp.gcpIntegration.projectidFieldLabel', { defaultMessage: 'Project ID', }), type: 'text', }, - credentials_file: { + 'gcp.credentials.file': { label: i18n.translate('xpack.csp.findings.gcpIntegration.gcpInputText.credentialFileText', { defaultMessage: 'Path to JSON file containing the credentials and key used to subscribe', }), type: 'text', }, - credentials_json: { + 'gcp.credentials.json': { label: i18n.translate('xpack.csp.findings.gcpIntegration.gcpInputText.credentialJSONText', { defaultMessage: 'JSON blob containing the credentials and key used to subscribe', }), type: 'text', }, - credentials_type: { + 'gcp.credentials.type': { label: i18n.translate( 'xpack.csp.findings.gcpIntegration.gcpInputText.credentialSelectBoxTitle', - { defaultMessage: 'Credential' } + { + defaultMessage: 'Credential', + } ), type: 'text', }, }, }; -const credentialOptionsList = [ - { - text: i18n.translate('xpack.csp.gcpIntegration.credentialsFileOption', { - defaultMessage: 'Credentials File', - }), - value: 'credentials-file', - }, - { - text: i18n.translate('xpack.csp.gcpIntegration.credentialsJsonOption', { - defaultMessage: 'Credentials JSON', - }), - value: 'credentials-json', - }, -]; - const getSetupFormatOptions = (): Array<{ id: SetupFormatGCP; label: string; @@ -193,10 +195,7 @@ interface GcpFormProps { onChange: any; } -const getInputVarsFields = ( - input: NewPackagePolicyInput, - fields: GcpInputFields[keyof GcpInputFields] -) => +const getInputVarsFields = (input: NewPackagePolicyInput, fields: GcpFields) => Object.entries(input.streams[0].vars || {}) .filter(([id]) => id in fields) .map(([id, inputVar]) => { @@ -348,7 +347,7 @@ export const GcpCredentialsForm = ({ // Integration is Invalid IF Version is not at least 1.5.0 OR Setup Access is manual but Project ID is empty useEffect(() => { const isProjectIdEmpty = - setupFormat === SETUP_ACCESS_MANUAL && !getFieldById('project_id')?.value; + setupFormat === SETUP_ACCESS_MANUAL && !getFieldById('gcp.project_id')?.value; const isInvalidPolicy = isInvalid || isProjectIdEmpty; setIsValid(!isInvalidPolicy); @@ -411,19 +410,21 @@ const GcpInputVarFields = ({ const getFieldById = (id: keyof GcpInputFields['fields']) => { return fields.find((element) => element.id === id); }; - const projectIdFields = getFieldById('project_id'); - const credentialsTypeFields = getFieldById('credentials_type') || credentialOptionsList[0]; - const credentialFilesFields = getFieldById('credentials_file'); - const credentialJSONFields = getFieldById('credentials_json'); + const projectIdFields = getFieldById('gcp.project_id'); + const credentialsTypeFields = getFieldById('gcp.credentials.type'); + const credentialFilesFields = getFieldById('gcp.credentials.file'); + const credentialJSONFields = getFieldById('gcp.credentials.json'); const credentialFieldValue = credentialOptionsList[0].value; const credentialJSONValue = credentialOptionsList[1].value; + const credentialsTypeValue = credentialsTypeFields?.value || credentialOptionsList[0].value; + return (
    {projectIdFields && ( - + )} - {credentialFilesFields && credentialJSONFields && ( - + {credentialsTypeFields && credentialFilesFields && credentialJSONFields && ( + { - onChange('credentials_type', optionElem.target.value); + onChange(credentialsTypeFields?.id, optionElem.target.value); }} /> )} - - {credentialsTypeFields.value === credentialFieldValue && credentialFilesFields && ( - + {credentialsTypeValue === credentialFieldValue && credentialFilesFields && ( + )} - {credentialsTypeFields?.value === credentialJSONValue && credentialJSONFields && ( - + {credentialsTypeValue === credentialJSONValue && credentialJSONFields && ( + { } as PackageInfo; }; -export const getMockPackageInfoCspmGCP = (packageVersion = '1.5.0') => { +export const getMockPackageInfoCspmGCP = (packageVersion = '1.5.2') => { return { version: packageVersion, name: 'cspm', @@ -130,9 +130,10 @@ const getPolicyMock = ( }; const gcpVarsMock = { - project_id: { type: 'text' }, - credentials_file: { type: 'text' }, - credentials_json: { type: 'text' }, + 'gcp.project_id': { type: 'text' }, + 'gcp.credentials.file': { type: 'text' }, + 'gcp.credentials.json': { type: 'text' }, + 'gcp.credentials.type': { type: 'text' }, }; const dataStream = { type: 'logs', dataset: 'cloud_security_posture.findings' }; diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx index 213d03b95266f..c5d4c0e8de3e2 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx @@ -979,7 +979,7 @@ describe('', () => { }); describe('GCP Credentials input fields', () => { - it(`renders ${CLOUDBEAT_GCP} Not supported when version is not at least version 1.5.0`, () => { + it(`renders ${CLOUDBEAT_GCP} Not supported when version is not at least version 1.5.2`, () => { let policy = getMockPolicyGCP(); policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { credentials_type: { value: 'credentials-file' }, @@ -1024,7 +1024,7 @@ describe('', () => { it(`project ID is required for Manual users`, () => { let policy = getMockPolicyGCP(); policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { - project_id: { value: undefined }, + 'gcp.project_id': { value: undefined }, setup_access: { value: 'manual' }, }); @@ -1036,7 +1036,7 @@ describe('', () => { updatedPolicy: policy, }); policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { - project_id: { value: '' }, + 'gcp.project_id': { value: '' }, setup_access: { value: 'manual' }, }); rerender(); @@ -1049,7 +1049,7 @@ describe('', () => { it(`renders ${CLOUDBEAT_GCP} Credentials File fields`, () => { let policy = getMockPolicyGCP(); policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { - credentials_type: { value: 'credentials-file' }, + 'gcp.credentials.type': { value: 'credentials-file' }, setup_access: { value: 'manual' }, }); @@ -1067,8 +1067,8 @@ describe('', () => { it(`updates ${CLOUDBEAT_GCP} Credentials File fields`, () => { let policy = getMockPolicyGCP(); policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { - project_id: { value: 'a' }, - credentials_type: { value: 'credentials-file' }, + 'gcp.project_id': { value: 'a' }, + 'gcp.credentials.type': { value: 'credentials-file' }, setup_access: { value: 'manual' }, }); @@ -1079,7 +1079,7 @@ describe('', () => { userEvent.type(getByTestId(CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS.CREDENTIALS_FILE), 'b'); policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { - credentials_file: { value: 'b' }, + 'gcp.credentials.file': { value: 'b' }, }); expect(onChange).toHaveBeenCalledWith({ @@ -1092,7 +1092,7 @@ describe('', () => { let policy = getMockPolicyGCP(); policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { setup_access: { value: 'manual' }, - credentials_type: { value: 'credentials-json' }, + 'gcp.credentials.type': { value: 'credentials-json' }, }); const { getByRole, getByLabelText } = render( @@ -1109,8 +1109,8 @@ describe('', () => { it(`updates ${CLOUDBEAT_GCP} Credentials JSON fields`, () => { let policy = getMockPolicyGCP(); policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { - project_id: { value: 'a' }, - credentials_type: { value: 'credentials-json' }, + 'gcp.project_id': { value: 'a' }, + 'gcp.credentials.type': { value: 'credentials-json' }, setup_access: { value: 'manual' }, }); @@ -1121,7 +1121,7 @@ describe('', () => { userEvent.type(getByTestId(CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS.CREDENTIALS_JSON), 'b'); policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { - credentials_json: { value: 'b' }, + 'gcp.credentials.json': { value: 'b' }, }); expect(onChange).toHaveBeenCalledWith({ diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities.tsx index 31b1efe73b453..a3ae53a25f4d9 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities.tsx @@ -17,7 +17,6 @@ import { } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { CspVulnerabilityFinding } from '../../../../common/schemas'; import { LATEST_VULNERABILITIES_INDEX_PATTERN } from '../../../../common/constants'; -import { getSafeVulnerabilitiesQueryFilter } from '../../../../common/utils/get_safe_vulnerabilities_query_filter'; import { useKibana } from '../../../common/hooks/use_kibana'; import { showErrorToast } from '../../../common/utils/show_error_toast'; import { FindingsBaseEsQuery } from '../../../common/types'; @@ -37,7 +36,7 @@ interface VulnerabilitiesQuery extends FindingsBaseEsQuery { export const getFindingsQuery = ({ query, sort, pageIndex, pageSize }: VulnerabilitiesQuery) => ({ index: LATEST_VULNERABILITIES_INDEX_PATTERN, - query: getSafeVulnerabilitiesQueryFilter(query), + query, from: pageIndex * pageSize, size: pageSize, sort, diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities_by_resource.ts b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities_by_resource.ts index 706f980408d18..3aa152d427143 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities_by_resource.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities_by_resource.ts @@ -21,7 +21,6 @@ import { LATEST_VULNERABILITIES_INDEX_PATTERN, VULNERABILITIES_SEVERITY, } from '../../../../common/constants'; -import { getSafeVulnerabilitiesQueryFilter } from '../../../../common/utils/get_safe_vulnerabilities_query_filter'; import { MAX_FINDINGS_TO_LOAD } from '../../../common/constants'; import { useKibana } from '../../../common/hooks/use_kibana'; @@ -60,7 +59,7 @@ export const getQuery = ({ pageSize, }: VulnerabilitiesQuery) => ({ index: LATEST_VULNERABILITIES_INDEX_PATTERN, - query: getSafeVulnerabilitiesQueryFilter(query), + query, aggs: { total: { cardinality: { field: 'resource.id' } }, resources: { diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities.tsx index 9e241364f6c01..d0b6412eaa393 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities.tsx @@ -20,7 +20,10 @@ import React, { useCallback, useMemo, useState, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { Routes, Route } from '@kbn/shared-ux-router'; import { LOCAL_STORAGE_PAGE_SIZE_FINDINGS_KEY } from '../../common/constants'; -import { useCloudPostureTable } from '../../common/hooks/use_cloud_posture_table'; +import { + CloudPostureTableResult, + useCloudPostureTable, +} from '../../common/hooks/use_cloud_posture_table'; import { useLatestVulnerabilities } from './hooks/use_latest_vulnerabilities'; import type { VulnerabilitiesQueryData } from './types'; import { LATEST_VULNERABILITIES_INDEX_PATTERN } from '../../../common/constants'; @@ -63,67 +66,35 @@ const getDefaultQuery = ({ query, filters }: any): any => ({ pageIndex: 0, }); -export const Vulnerabilities = () => { - const { data, isLoading, error } = useFilteredDataView(LATEST_VULNERABILITIES_INDEX_PATTERN); - const getSetupStatus = useCspSetupStatusApi(); - - if (getSetupStatus?.data?.vuln_mgmt?.status !== 'indexed') return ; - - if (error) { - return ; - } - if (isLoading) { - return defaultLoadingRenderer(); - } - - if (!data) { - return defaultNoDataRenderer(); - } - - return ( - - } - /> - } - /> - } - /> - - ); -}; - const VulnerabilitiesDataGrid = ({ dataView, data, isFetching, + onChangeItemsPerPage, + onChangePage, + onSort, + urlQuery, + onResetFilters, + pageSize, + setUrlQuery, + pageIndex, + sort, }: { dataView: DataView; data: VulnerabilitiesQueryData | undefined; isFetching: boolean; -}) => { - const { - pageIndex, - sort, - pageSize, - onChangeItemsPerPage, - onChangePage, - onSort, - urlQuery, - setUrlQuery, - onResetFilters, - } = useCloudPostureTable({ - dataView, - defaultQuery: getDefaultQuery, - paginationLocalStorageKey: LOCAL_STORAGE_PAGE_SIZE_FINDINGS_KEY, - }); +} & Pick< + CloudPostureTableResult, + | 'pageIndex' + | 'sort' + | 'pageSize' + | 'onChangeItemsPerPage' + | 'onChangePage' + | 'onSort' + | 'urlQuery' + | 'setUrlQuery' + | 'onResetFilters' +>) => { const { euiTheme } = useEuiTheme(); const styles = useStyles(); const [showHighlight, setHighlight] = useState(false); @@ -131,7 +102,9 @@ const VulnerabilitiesDataGrid = ({ const invalidIndex = -1; const selectedVulnerability = useMemo(() => { - return data?.page[urlQuery.vulnerabilityIndex]; + if (urlQuery.vulnerabilityIndex !== undefined) { + return data?.page[urlQuery.vulnerabilityIndex]; + } }, [data?.page, urlQuery.vulnerabilityIndex]); const onCloseFlyout = () => { @@ -192,7 +165,9 @@ const VulnerabilitiesDataGrid = ({ const flyoutVulnerabilityIndex = urlQuery?.vulnerabilityIndex; - const selectedVulnerabilityIndex = flyoutVulnerabilityIndex + pageIndex * pageSize; + const selectedVulnerabilityIndex = flyoutVulnerabilityIndex + ? flyoutVulnerabilityIndex + pageIndex * pageSize + : undefined; const renderCellValue = useMemo(() => { const Cell: React.FC = ({ @@ -304,7 +279,9 @@ const VulnerabilitiesDataGrid = ({ [pageSize, setUrlQuery] ); - const showVulnerabilityFlyout = flyoutVulnerabilityIndex > invalidIndex; + const showVulnerabilityFlyout = flyoutVulnerabilityIndex + ? flyoutVulnerabilityIndex > invalidIndex + : undefined; if (data?.page.length === 0) { return ; @@ -390,8 +367,21 @@ const VulnerabilitiesDataGrid = ({ ); }; + const VulnerabilitiesContent = ({ dataView }: { dataView: DataView }) => { - const { pageIndex, query, sort, queryError, pageSize, setUrlQuery } = useCloudPostureTable({ + const { + sort, + query, + queryError, + pageSize, + pageIndex, + onChangeItemsPerPage, + onChangePage, + onSort, + urlQuery, + setUrlQuery, + onResetFilters, + } = useCloudPostureTable({ dataView, defaultQuery: getDefaultQuery, paginationLocalStorageKey: LOCAL_STORAGE_PAGE_SIZE_FINDINGS_KEY, @@ -443,8 +433,58 @@ const VulnerabilitiesContent = ({ dataView }: { dataView: DataView }) => { {error && } {!error && ( - + )} ); }; + +export const Vulnerabilities = () => { + const { data, isLoading, error } = useFilteredDataView(LATEST_VULNERABILITIES_INDEX_PATTERN); + const getSetupStatus = useCspSetupStatusApi(); + + if (getSetupStatus?.data?.vuln_mgmt?.status !== 'indexed') return ; + + if (error) { + return ; + } + if (isLoading) { + return defaultLoadingRenderer(); + } + + if (!data) { + return defaultNoDataRenderer(); + } + + return ( + + } + /> + } + /> + } + /> + + ); +}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_by_resource/resource_vulnerabilities/resource_vulnerabilities.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_by_resource/resource_vulnerabilities/resource_vulnerabilities.tsx index 2f1389015e907..673dd2e8130e4 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_by_resource/resource_vulnerabilities/resource_vulnerabilities.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_by_resource/resource_vulnerabilities/resource_vulnerabilities.tsx @@ -21,7 +21,10 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { Link, useParams, generatePath } from 'react-router-dom'; import type { BoolQuery } from '@kbn/es-query'; import { LOCAL_STORAGE_PAGE_SIZE_FINDINGS_KEY } from '../../../../common/constants'; -import { useCloudPostureTable } from '../../../../common/hooks/use_cloud_posture_table'; +import { + CloudPostureTableResult, + useCloudPostureTable, +} from '../../../../common/hooks/use_cloud_posture_table'; import { useLatestVulnerabilities } from '../../hooks/use_latest_vulnerabilities'; import type { VulnerabilitiesQueryData } from '../../types'; import { ErrorCallout } from '../../../configurations/layout/error_callout'; @@ -68,26 +71,31 @@ const ResourceVulnerabilitiesDataGrid = ({ dataView, data, isFetching, + pageIndex, + sort, + pageSize, + onChangeItemsPerPage, + onChangePage, + onSort, + urlQuery, + setUrlQuery, + onResetFilters, }: { dataView: DataView; data: VulnerabilitiesQueryData; isFetching: boolean; -}) => { - const { - pageIndex, - sort, - pageSize, - onChangeItemsPerPage, - onChangePage, - onSort, - urlQuery, - setUrlQuery, - onResetFilters, - } = useCloudPostureTable({ - dataView, - defaultQuery: getDefaultQuery, - paginationLocalStorageKey: LOCAL_STORAGE_PAGE_SIZE_FINDINGS_KEY, - }); +} & Pick< + CloudPostureTableResult, + | 'pageIndex' + | 'sort' + | 'pageSize' + | 'onChangeItemsPerPage' + | 'onChangePage' + | 'onSort' + | 'urlQuery' + | 'setUrlQuery' + | 'onResetFilters' +>) => { const { euiTheme } = useEuiTheme(); const styles = useStyles(); @@ -355,7 +363,19 @@ export const ResourceVulnerabilities = ({ dataView }: { dataView: DataView }) => const params = useParams<{ resourceId: string }>(); const resourceId = decodeURIComponent(params.resourceId); - const { pageIndex, query, sort, queryError, pageSize, setUrlQuery } = useCloudPostureTable({ + const { + pageIndex, + pageSize, + onChangeItemsPerPage, + onChangePage, + query, + sort, + onSort, + queryError, + urlQuery, + setUrlQuery, + onResetFilters, + } = useCloudPostureTable({ dataView, defaultQuery: getDefaultQuery, paginationLocalStorageKey: LOCAL_STORAGE_PAGE_SIZE_FINDINGS_KEY, @@ -457,7 +477,20 @@ export const ResourceVulnerabilities = ({ dataView }: { dataView: DataView }) => {error && } {!error && ( - + )} ); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_by_resource/vulnerabilities_by_resource.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_by_resource/vulnerabilities_by_resource.tsx index caa5cca3a3ac1..89488bf52046b 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_by_resource/vulnerabilities_by_resource.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_by_resource/vulnerabilities_by_resource.tsx @@ -19,7 +19,10 @@ import { i18n } from '@kbn/i18n'; import { Link, generatePath } from 'react-router-dom'; import { LOCAL_STORAGE_PAGE_SIZE_FINDINGS_KEY } from '../../../common/constants'; import { findingsNavigation } from '../../../common/navigation/constants'; -import { useCloudPostureTable } from '../../../common/hooks/use_cloud_posture_table'; +import { + CloudPostureTableResult, + useCloudPostureTable, +} from '../../../common/hooks/use_cloud_posture_table'; import { ErrorCallout } from '../../configurations/layout/error_callout'; import { FindingsSearchBar } from '../../configurations/layout/findings_search_bar'; import { useLimitProperties } from '../../../common/utils/get_limit_properties'; @@ -54,26 +57,31 @@ const VulnerabilitiesByResourceDataGrid = ({ dataView, data, isFetching, + pageIndex, + sort, + pageSize, + onChangeItemsPerPage, + onChangePage, + onSort, + urlQuery, + setUrlQuery, + onResetFilters, }: { dataView: DataView; data: VulnerabilitiesByResourceQueryData | undefined; isFetching: boolean; -}) => { - const { - pageIndex, - sort, - pageSize, - onChangeItemsPerPage, - onChangePage, - onSort, - urlQuery, - setUrlQuery, - onResetFilters, - } = useCloudPostureTable({ - dataView, - defaultQuery: getDefaultQuery, - paginationLocalStorageKey: LOCAL_STORAGE_PAGE_SIZE_FINDINGS_KEY, - }); +} & Pick< + CloudPostureTableResult, + | 'pageIndex' + | 'sort' + | 'pageSize' + | 'onChangeItemsPerPage' + | 'onChangePage' + | 'onSort' + | 'urlQuery' + | 'setUrlQuery' + | 'onResetFilters' +>) => { const styles = useStyles(); const { isLastLimitedPage, limitedTotalItemCount } = useLimitProperties({ @@ -232,7 +240,19 @@ const VulnerabilitiesByResourceDataGrid = ({ }; export const VulnerabilitiesByResource = ({ dataView }: { dataView: DataView }) => { - const { pageIndex, query, sort, queryError, pageSize, setUrlQuery } = useCloudPostureTable({ + const { + pageIndex, + onChangeItemsPerPage, + onChangePage, + pageSize, + query, + sort, + onSort, + queryError, + urlQuery, + setUrlQuery, + onResetFilters, + } = useCloudPostureTable({ dataView, defaultQuery: getDefaultQuery, paginationLocalStorageKey: LOCAL_STORAGE_PAGE_SIZE_FINDINGS_KEY, @@ -273,6 +293,15 @@ export const VulnerabilitiesByResource = ({ dataView }: { dataView: DataView }) dataView={dataView} data={data} isFetching={isFetching} + pageIndex={pageIndex} + sort={sort} + pageSize={pageSize} + onChangeItemsPerPage={onChangeItemsPerPage} + onChangePage={onChangePage} + onSort={onSort} + urlQuery={urlQuery} + setUrlQuery={setUrlQuery} + onResetFilters={onResetFilters} /> )} diff --git a/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_top_patchable_vulnerabilities.ts b/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_top_patchable_vulnerabilities.ts index 9863b865c7542..6eec795e2dad6 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_top_patchable_vulnerabilities.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_top_patchable_vulnerabilities.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { QueryDslQueryContainer, SearchRequest } from '@elastic/elasticsearch/lib/api/types'; +import { SearchRequest } from '@elastic/elasticsearch/lib/api/types'; import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import { AggFieldBucket, PatchableVulnerabilityStat } from '../../../common/types'; import { LATEST_VULNERABILITIES_INDEX_DEFAULT_NS } from '../../../common/constants'; @@ -26,9 +26,19 @@ export interface PatchableVulnerabilitiesQueryResult { }; } -const getPatchableVulnerabilitiesQuery = (query: QueryDslQueryContainer): SearchRequest => ({ +const getPatchableVulnerabilitiesQuery = (): SearchRequest => ({ size: 0, - query, + query: { + bool: { + filter: [ + { + exists: { + field: 'package.fixed_version', + }, + }, + ], + }, + }, index: LATEST_VULNERABILITIES_INDEX_DEFAULT_NS, aggs: { patchable_vulnerabilities: { @@ -64,24 +74,10 @@ const getPatchableVulnerabilitiesQuery = (query: QueryDslQueryContainer): Search }); export const getTopPatchableVulnerabilities = async ( - esClient: ElasticsearchClient, - query: QueryDslQueryContainer + esClient: ElasticsearchClient ): Promise => { const queryResult = await esClient.search( - getPatchableVulnerabilitiesQuery({ - ...query, - bool: { - ...query.bool, - filter: [ - ...(query.bool?.filter as QueryDslQueryContainer[]), - { - exists: { - field: 'package.fixed_version', - }, - }, - ], - }, - }) + getPatchableVulnerabilitiesQuery() ); if (!queryResult?.aggregations?.patchable_vulnerabilities) return []; diff --git a/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_top_vulnerabilities.ts b/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_top_vulnerabilities.ts index 962b4135ad96f..cd8c5b64f532d 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_top_vulnerabilities.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_top_vulnerabilities.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { QueryDslQueryContainer, SearchRequest } from '@elastic/elasticsearch/lib/api/types'; +import { SearchRequest } from '@elastic/elasticsearch/lib/api/types'; import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import { VulnerabilityStat } from '../../../common/types'; import { LATEST_VULNERABILITIES_INDEX_DEFAULT_NS } from '../../../common/constants'; @@ -72,9 +72,11 @@ export interface VulnerabilitiesQueryResult { }; } -const getVulnerabilitiesQuery = (query: QueryDslQueryContainer): SearchRequest => ({ +const getVulnerabilitiesQuery = (): SearchRequest => ({ size: 0, - query, + query: { + match_all: {}, + }, index: LATEST_VULNERABILITIES_INDEX_DEFAULT_NS, aggs: { vulnerabilities: { @@ -126,11 +128,10 @@ const getVulnerabilitiesQuery = (query: QueryDslQueryContainer): SearchRequest = }); export const getTopVulnerabilities = async ( - esClient: ElasticsearchClient, - query: QueryDslQueryContainer + esClient: ElasticsearchClient ): Promise => { const queryResult = await esClient.search( - getVulnerabilitiesQuery(query) + getVulnerabilitiesQuery() ); if (!queryResult?.aggregations?.vulnerabilities) return []; diff --git a/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_top_vulnerable_resources.ts b/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_top_vulnerable_resources.ts index 1753e3499fee3..0a41a11aeedea 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_top_vulnerable_resources.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_top_vulnerable_resources.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { QueryDslQueryContainer, SearchRequest } from '@elastic/elasticsearch/lib/api/types'; +import { SearchRequest } from '@elastic/elasticsearch/lib/api/types'; import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import { AggFieldBucket, VulnerableResourceStat } from '../../../common/types'; import { LATEST_VULNERABILITIES_INDEX_DEFAULT_NS } from '../../../common/constants'; @@ -23,9 +23,11 @@ export interface VulnerableResourcesQueryResult { }; } -const getVulnerabilitiesResourcesQuery = (query: QueryDslQueryContainer): SearchRequest => ({ +const getVulnerabilitiesResourcesQuery = (): SearchRequest => ({ size: 0, - query, + query: { + match_all: {}, + }, index: LATEST_VULNERABILITIES_INDEX_DEFAULT_NS, aggs: { vulnerable_resources: { @@ -55,11 +57,10 @@ const getVulnerabilitiesResourcesQuery = (query: QueryDslQueryContainer): Search }); export const getTopVulnerableResources = async ( - esClient: ElasticsearchClient, - query: QueryDslQueryContainer + esClient: ElasticsearchClient ): Promise => { const queryResult = await esClient.search( - getVulnerabilitiesResourcesQuery(query) + getVulnerabilitiesResourcesQuery() ); if (!queryResult?.aggregations?.vulnerable_resources) return []; diff --git a/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_vulnerabilities_statistics.ts b/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_vulnerabilities_statistics.ts index 5139e3cb92ba9..8458d66817f82 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_vulnerabilities_statistics.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_vulnerabilities_statistics.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { QueryDslQueryContainer, SearchRequest } from '@elastic/elasticsearch/lib/api/types'; +import { SearchRequest } from '@elastic/elasticsearch/lib/api/types'; import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import { LATEST_VULNERABILITIES_INDEX_DEFAULT_NS, @@ -30,11 +30,11 @@ export interface VulnerabilitiesStatisticsQueryResult { }; } -export const getVulnerabilitiesStatisticsQuery = ( - query: QueryDslQueryContainer -): SearchRequest => ({ +export const getVulnerabilitiesStatisticsQuery = (): SearchRequest => ({ size: 0, - query, + query: { + match_all: {}, + }, index: LATEST_VULNERABILITIES_INDEX_DEFAULT_NS, aggs: { critical: { @@ -59,12 +59,9 @@ export const getVulnerabilitiesStatisticsQuery = ( }, }); -export const getVulnerabilitiesStatistics = async ( - esClient: ElasticsearchClient, - query: QueryDslQueryContainer -) => { +export const getVulnerabilitiesStatistics = async (esClient: ElasticsearchClient) => { const queryResult = await esClient.search( - getVulnerabilitiesStatisticsQuery(query) + getVulnerabilitiesStatisticsQuery() ); return { diff --git a/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/vulnerabilities_dashboard.ts b/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/vulnerabilities_dashboard.ts index be45fefeccd3d..e77851062217c 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/vulnerabilities_dashboard.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/vulnerabilities_dashboard.ts @@ -9,18 +9,12 @@ import { transformError } from '@kbn/securitysolution-es-utils'; import { getVulnerabilitiesTrends } from './get_vulnerabilities_trend'; import type { CnvmDashboardData } from '../../../common/types'; import { VULNERABILITIES_DASHBOARD_ROUTE_PATH } from '../../../common/constants'; -import { getSafeVulnerabilitiesQueryFilter } from '../../../common/utils/get_safe_vulnerabilities_query_filter'; import { CspRouter } from '../../types'; import { getVulnerabilitiesStatistics } from './get_vulnerabilities_statistics'; import { getTopVulnerableResources } from './get_top_vulnerable_resources'; import { getTopPatchableVulnerabilities } from './get_top_patchable_vulnerabilities'; import { getTopVulnerabilities } from './get_top_vulnerabilities'; -export interface KeyDocCount { - key: TKey; - doc_count: number; -} - export const defineGetVulnerabilitiesDashboardRoute = (router: CspRouter): void => router.get( { @@ -36,8 +30,6 @@ export const defineGetVulnerabilitiesDashboardRoute = (router: CspRouter): void try { const esClient = cspContext.esClient.asCurrentUser; - const query = getSafeVulnerabilitiesQueryFilter(); - const [ cnvmStatistics, vulnTrends, @@ -45,11 +37,11 @@ export const defineGetVulnerabilitiesDashboardRoute = (router: CspRouter): void topPatchableVulnerabilities, topVulnerabilities, ] = await Promise.all([ - getVulnerabilitiesStatistics(esClient, query), + getVulnerabilitiesStatistics(esClient), getVulnerabilitiesTrends(esClient), - getTopVulnerableResources(esClient, query), - getTopPatchableVulnerabilities(esClient, query), - getTopVulnerabilities(esClient, query), + getTopVulnerableResources(esClient), + getTopPatchableVulnerabilities(esClient), + getTopVulnerabilities(esClient), ]); const body: CnvmDashboardData = { diff --git a/x-pack/plugins/cloud_security_posture/server/tasks/findings_stats_task.ts b/x-pack/plugins/cloud_security_posture/server/tasks/findings_stats_task.ts index bec5aacd9f95c..f40ce3f7dc4ab 100644 --- a/x-pack/plugins/cloud_security_posture/server/tasks/findings_stats_task.ts +++ b/x-pack/plugins/cloud_security_posture/server/tasks/findings_stats_task.ts @@ -14,7 +14,6 @@ import { import { SearchRequest } from '@kbn/data-plugin/common'; import { ElasticsearchClient } from '@kbn/core/server'; import type { Logger } from '@kbn/core/server'; -import { getSafeVulnerabilitiesQueryFilter } from '../../common/utils/get_safe_vulnerabilities_query_filter'; import { getSafePostureTypeRuntimeMapping } from '../../common/runtime_mappings/get_safe_posture_type_runtime_mapping'; import { getIdentifierRuntimeMapping } from '../../common/runtime_mappings/get_identifier_runtime_mapping'; import { FindingsStatsTaskResult, ScoreByPolicyTemplateBucket, VulnSeverityAggs } from './types'; @@ -186,7 +185,9 @@ const getScoreQuery = (): SearchRequest => ({ const getVulnStatsTrendQuery = (): SearchRequest => ({ index: LATEST_VULNERABILITIES_INDEX_DEFAULT_NS, size: 0, - query: getSafeVulnerabilitiesQueryFilter(), + query: { + match_all: {}, + }, aggs: { critical: { filter: { term: { 'vulnerability.severity': VULNERABILITIES_SEVERITY.CRITICAL } }, diff --git a/x-pack/plugins/elastic_assistant/README.md b/x-pack/plugins/elastic_assistant/README.md new file mode 100755 index 0000000000000..e0fef329abc08 --- /dev/null +++ b/x-pack/plugins/elastic_assistant/README.md @@ -0,0 +1,9 @@ +# Elastic AI Assistant + +This plugin implements (only) server APIs for the `Elastic AI Assistant`. + +This plugin does NOT contain UI components. See `x-pack/packages/kbn-elastic-assistant` for React components. + +## Maintainers + +Maintained by the Security Solution team diff --git a/x-pack/plugins/elastic_assistant/common/constants.ts b/x-pack/plugins/elastic_assistant/common/constants.ts new file mode 100755 index 0000000000000..c6c6f2419a182 --- /dev/null +++ b/x-pack/plugins/elastic_assistant/common/constants.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const PLUGIN_ID = 'elasticAssistant'; +export const PLUGIN_NAME = 'elasticAssistant'; + +export const BASE_PATH = '/internal/elastic_assistant'; + +export const POST_ACTIONS_CONNECTOR_EXECUTE = `${BASE_PATH}/actions/connector/{connectorId}/_execute`; diff --git a/x-pack/plugins/elastic_assistant/jest.config.js b/x-pack/plugins/elastic_assistant/jest.config.js new file mode 100644 index 0000000000000..7bbeb3b1c89d2 --- /dev/null +++ b/x-pack/plugins/elastic_assistant/jest.config.js @@ -0,0 +1,17 @@ +/* + * 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. + */ + +module.exports = { + collectCoverageFrom: [ + '/x-pack/plugins/elastic_assistant/{common,lib,server}/**/*.{ts,tsx}', + ], + coverageDirectory: '/target/kibana-coverage/jest/x-pack/plugins/elastic_assistant', + coverageReporters: ['text', 'html'], + rootDir: '../../..', + roots: ['/x-pack/plugins/elastic_assistant'], + preset: '@kbn/test', +}; diff --git a/x-pack/plugins/elastic_assistant/kibana.jsonc b/x-pack/plugins/elastic_assistant/kibana.jsonc new file mode 100644 index 0000000000000..d7518cf600983 --- /dev/null +++ b/x-pack/plugins/elastic_assistant/kibana.jsonc @@ -0,0 +1,15 @@ +{ + "type": "plugin", + "id": "@kbn/elastic-assistant-plugin", + "owner": "@elastic/security-solution", + "description": "Server APIs for the Elastic AI Assistant", + "plugin": { + "id": "elasticAssistant", + "server": true, + "browser": false, + "requiredPlugins": [ + "actions", + "data" + ] + } +} diff --git a/x-pack/plugins/elastic_assistant/server/__mocks__/action_result_data.ts b/x-pack/plugins/elastic_assistant/server/__mocks__/action_result_data.ts new file mode 100644 index 0000000000000..280a86d2ac326 --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/__mocks__/action_result_data.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/** + * A mock `data` property from an `actionResult` response, which is returned + * from the `execute` method of the Actions plugin. + * + * Given the following example: + * + * ```ts + * const actionResult = await actionsClient.execute(requestBody); + * ``` + * + * In the above example, `actionResult.data` would be this mock data. + */ +export const mockActionResultData = { + id: 'chatcmpl-7sFVvksgFtMUac3pY5bTypFAKaGX1', + object: 'chat.completion', + created: 1693163703, + model: 'gpt-4', + choices: [ + { + index: 0, + finish_reason: 'stop', + message: { + role: 'assistant', + content: 'Yes, your name is Andrew. How can I assist you further, Andrew?', + }, + }, + ], + usage: { completion_tokens: 16, prompt_tokens: 140, total_tokens: 156 }, +}; diff --git a/x-pack/plugins/elastic_assistant/server/__mocks__/lang_chain_messages.ts b/x-pack/plugins/elastic_assistant/server/__mocks__/lang_chain_messages.ts new file mode 100644 index 0000000000000..bbf32c714065f --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/__mocks__/lang_chain_messages.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { AIMessage, BaseMessage, HumanMessage } from 'langchain/schema'; + +export const langChainMessages: BaseMessage[] = [ + new HumanMessage('What is my name?'), + new AIMessage( + "I'm sorry, but I am not able to answer questions unrelated to Elastic Security. If you have any questions about Elastic Security, please feel free to ask." + ), + new HumanMessage('\n\nMy name is Andrew'), + new AIMessage( + "Hello Andrew! If you have any questions about Elastic Security, feel free to ask, and I'll do my best to help you." + ), + new HumanMessage('\n\nDo you know my name?'), +]; diff --git a/x-pack/plugins/elastic_assistant/server/__mocks__/request.ts b/x-pack/plugins/elastic_assistant/server/__mocks__/request.ts new file mode 100644 index 0000000000000..827f08683e0b8 --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/__mocks__/request.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { httpServerMock } from '@kbn/core/server/mocks'; + +export const requestMock = { + create: httpServerMock.createKibanaRequest, +}; diff --git a/x-pack/plugins/elastic_assistant/server/__mocks__/request_context.ts b/x-pack/plugins/elastic_assistant/server/__mocks__/request_context.ts new file mode 100644 index 0000000000000..19fb44f7f8bac --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/__mocks__/request_context.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { coreMock } from '@kbn/core/server/mocks'; +import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; + +export const createMockClients = () => { + const core = coreMock.createRequestHandlerContext(); + const license = licensingMock.createLicenseMock(); + + return { + core, + clusterClient: core.elasticsearch.client, + savedObjectsClient: core.savedObjects.client, + + licensing: { + ...licensingMock.createRequestHandlerContext({ license }), + license, + }, + + config: createMockConfig(), + appClient: createAppClientMock(), + }; +}; + +type MockClients = ReturnType; + +const convertRequestContextMock = (context: T) => { + return coreMock.createCustomRequestHandlerContext(context); +}; + +const createMockConfig = () => ({}); + +const createAppClientMock = () => ({}); + +const createRequestContextMock = (clients: MockClients = createMockClients()) => { + return { + core: clients.core, + }; +}; + +const createTools = () => { + const clients = createMockClients(); + const context = createRequestContextMock(clients); + + return { clients, context }; +}; + +export const requestContextMock = { + create: createRequestContextMock, + convertContext: convertRequestContextMock, + createTools, +}; diff --git a/x-pack/plugins/elastic_assistant/server/__mocks__/response.ts b/x-pack/plugins/elastic_assistant/server/__mocks__/response.ts new file mode 100644 index 0000000000000..8efe2407f2245 --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/__mocks__/response.ts @@ -0,0 +1,12 @@ +/* + * 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 { httpServerMock } from '@kbn/core/server/mocks'; + +export const responseMock = { + create: httpServerMock.createResponseFactory, +}; diff --git a/x-pack/plugins/elastic_assistant/server/__mocks__/server.ts b/x-pack/plugins/elastic_assistant/server/__mocks__/server.ts new file mode 100644 index 0000000000000..7ac44e1beedf1 --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/__mocks__/server.ts @@ -0,0 +1,95 @@ +/* + * 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 { httpServiceMock } from '@kbn/core/server/mocks'; +import type { RequestHandler, RouteConfig, KibanaRequest } from '@kbn/core/server'; +import type { RequestHandlerContext } from '@kbn/core-http-request-handler-context-server'; + +import { requestMock } from './request'; +import { responseMock as responseFactoryMock } from './response'; +import { requestContextMock } from './request_context'; +import { responseAdapter } from './test_adapters'; + +interface Route { + config: RouteConfig; + handler: RequestHandler; +} + +const getRoute = (routerMock: MockServer['router']): Route => { + const routeCalls = [ + ...routerMock.get.mock.calls, + ...routerMock.post.mock.calls, + ...routerMock.put.mock.calls, + ...routerMock.patch.mock.calls, + ...routerMock.delete.mock.calls, + ]; + + const [route] = routeCalls; + if (!route) { + throw new Error('No route registered!'); + } + + const [config, handler] = route; + return { config, handler }; +}; + +const buildResultMock = () => ({ ok: jest.fn((x) => x), badRequest: jest.fn((x) => x) }); + +class MockServer { + constructor( + public readonly router = httpServiceMock.createRouter(), + private responseMock = responseFactoryMock.create(), + private contextMock = requestContextMock.convertContext(requestContextMock.create()), + private resultMock = buildResultMock() + ) {} + + public validate(request: KibanaRequest) { + this.validateRequest(request); + return this.resultMock; + } + + public async inject(request: KibanaRequest, context: RequestHandlerContext = this.contextMock) { + const validatedRequest = this.validateRequest(request); + const [rejection] = this.resultMock.badRequest.mock.calls; + if (rejection) { + throw new Error(`Request was rejected with message: '${rejection}'`); + } + + await this.getRoute().handler(context, validatedRequest, this.responseMock); + return responseAdapter(this.responseMock); + } + + private getRoute(): Route { + return getRoute(this.router); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private maybeValidate(part: any, validator?: any): any { + return typeof validator === 'function' ? validator(part, this.resultMock) : part; + } + + private validateRequest(request: KibanaRequest): KibanaRequest { + const validations = this.getRoute().config.validate; + if (!validations) { + return request; + } + + const validatedRequest = requestMock.create({ + path: request.route.path, + method: request.route.method, + body: this.maybeValidate(request.body, validations.body), + query: this.maybeValidate(request.query, validations.query), + params: this.maybeValidate(request.params, validations.params), + }); + + return validatedRequest; + } +} +const createMockServer = () => new MockServer(); + +export const serverMock = { + create: createMockServer, +}; diff --git a/x-pack/plugins/elastic_assistant/server/__mocks__/test_adapters.ts b/x-pack/plugins/elastic_assistant/server/__mocks__/test_adapters.ts new file mode 100644 index 0000000000000..4de81ca931692 --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/__mocks__/test_adapters.ts @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { responseMock } from './response'; + +type ResponseMock = ReturnType; +type Method = keyof ResponseMock; + +type MockCall = any; // eslint-disable-line @typescript-eslint/no-explicit-any + +interface ResponseCall { + body: any; // eslint-disable-line @typescript-eslint/no-explicit-any + status: number; +} + +/** + * @internal + */ +export interface Response extends ResponseCall { + calls: ResponseCall[]; +} + +const buildResponses = (method: Method, calls: MockCall[]): ResponseCall[] => { + if (!calls.length) return []; + + switch (method) { + case 'ok': + return calls.map(([call]) => ({ status: 200, body: call.body })); + case 'custom': + return calls.map(([call]) => ({ + status: call.statusCode, + body: JSON.parse(call.body), + })); + case 'customError': + return calls.map(([call]) => ({ + status: call.statusCode, + body: call.body, + })); + default: + throw new Error(`Encountered unexpected call to response.${method}`); + } +}; + +export const responseAdapter = (response: ResponseMock): Response => { + const methods = Object.keys(response) as Method[]; + const calls = methods + .reduce((responses, method) => { + const methodMock = response[method]; + return [...responses, ...buildResponses(method, methodMock.mock.calls)]; + }, []) + .sort((call, other) => other.status - call.status); + + const [{ body, status }] = calls; + + return { + body, + status, + calls, + }; +}; diff --git a/x-pack/plugins/elastic_assistant/server/index.ts b/x-pack/plugins/elastic_assistant/server/index.ts new file mode 100755 index 0000000000000..a375e036d8238 --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { PluginInitializerContext } from '@kbn/core/server'; +import { ElasticAssistantPlugin } from './plugin'; + +export function plugin(initializerContext: PluginInitializerContext) { + return new ElasticAssistantPlugin(initializerContext); +} + +export type { + ElasticAssistantPluginSetup as EcsDataQualityDashboardPluginSetup, + ElasticAssistantPluginStart as EcsDataQualityDashboardPluginStart, +} from './types'; diff --git a/x-pack/plugins/elastic_assistant/server/lib/build_response/index.ts b/x-pack/plugins/elastic_assistant/server/lib/build_response/index.ts new file mode 100644 index 0000000000000..bbec702c74915 --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/lib/build_response/index.ts @@ -0,0 +1,68 @@ +/* + * 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 type { CustomHttpResponseOptions, KibanaResponseFactory } from '@kbn/core-http-server'; + +const statusToErrorMessage = ( + statusCode: number +): + | 'Bad Request' + | 'Unauthorized' + | 'Forbidden' + | 'Not Found' + | 'Conflict' + | 'Internal Error' + | '(unknown error)' => { + switch (statusCode) { + case 400: + return 'Bad Request'; + case 401: + return 'Unauthorized'; + case 403: + return 'Forbidden'; + case 404: + return 'Not Found'; + case 409: + return 'Conflict'; + case 500: + return 'Internal Error'; + default: + return '(unknown error)'; + } +}; + +/** Creates responses */ +export class ResponseFactory { + /** constructor */ + constructor(private response: KibanaResponseFactory) {} + + /** error */ + error({ statusCode, body, headers }: CustomHttpResponseOptions) { + const contentType: CustomHttpResponseOptions['headers'] = { + 'content-type': 'application/json', + }; + const defaultedHeaders: CustomHttpResponseOptions['headers'] = { + ...contentType, + ...(headers ?? {}), + }; + + return this.response.custom({ + body: Buffer.from( + JSON.stringify({ + message: body ?? statusToErrorMessage(statusCode), + status_code: statusCode, + }) + ), + headers: defaultedHeaders, + statusCode, + }); + } +} + +/** builds a response */ +export const buildResponse = (response: KibanaResponseFactory): ResponseFactory => + new ResponseFactory(response); diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/execute_custom_llm_chain/index.test.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/execute_custom_llm_chain/index.test.ts new file mode 100644 index 0000000000000..b6c4dd3917585 --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/execute_custom_llm_chain/index.test.ts @@ -0,0 +1,105 @@ +/* + * 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 { KibanaRequest } from '@kbn/core/server'; +import { PluginStartContract as ActionsPluginStart } from '@kbn/actions-plugin/server'; + +import { ResponseBody } from '../helpers'; +import { ActionsClientLlm } from '../llm/actions_client_llm'; +import { mockActionResultData } from '../../../__mocks__/action_result_data'; +import { langChainMessages } from '../../../__mocks__/lang_chain_messages'; +import { executeCustomLlmChain } from '.'; + +jest.mock('../llm/actions_client_llm'); + +const mockConversationChain = { + call: jest.fn(), +}; + +jest.mock('langchain/chains', () => ({ + ConversationChain: jest.fn().mockImplementation(() => mockConversationChain), +})); + +const mockConnectorId = 'mock-connector-id'; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const mockRequest: KibanaRequest = {} as KibanaRequest< + unknown, + unknown, + any, // eslint-disable-line @typescript-eslint/no-explicit-any + any // eslint-disable-line @typescript-eslint/no-explicit-any +>; + +const mockActions: ActionsPluginStart = {} as ActionsPluginStart; + +describe('executeCustomLlmChain', () => { + beforeEach(() => { + jest.clearAllMocks(); + + ActionsClientLlm.prototype.getActionResultData = jest + .fn() + .mockReturnValueOnce(mockActionResultData); + }); + + it('creates an instance of ActionsClientLlm with the expected context from the request', async () => { + await executeCustomLlmChain({ + actions: mockActions, + connectorId: mockConnectorId, + langChainMessages, + request: mockRequest, + }); + + expect(ActionsClientLlm).toHaveBeenCalledWith({ + actions: mockActions, + connectorId: mockConnectorId, + request: mockRequest, + }); + }); + + it('kicks off the chain with (only) the last message', async () => { + await executeCustomLlmChain({ + actions: mockActions, + connectorId: mockConnectorId, + langChainMessages, + request: mockRequest, + }); + + expect(mockConversationChain.call).toHaveBeenCalledWith({ + input: '\n\nDo you know my name?', + }); + }); + + it('kicks off the chain with the expected message when langChainMessages has only one entry', async () => { + const onlyOneMessage = [langChainMessages[0]]; + + await executeCustomLlmChain({ + actions: mockActions, + connectorId: mockConnectorId, + langChainMessages: onlyOneMessage, + request: mockRequest, + }); + + expect(mockConversationChain.call).toHaveBeenCalledWith({ + input: 'What is my name?', + }); + }); + + it('returns the expected response body', async () => { + const result: ResponseBody = await executeCustomLlmChain({ + actions: mockActions, + connectorId: mockConnectorId, + langChainMessages, + request: mockRequest, + }); + + expect(result).toEqual({ + connector_id: 'mock-connector-id', + data: mockActionResultData, + status: 'ok', + }); + }); +}); diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/execute_custom_llm_chain/index.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/execute_custom_llm_chain/index.ts new file mode 100644 index 0000000000000..ee7b6820c983d --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/execute_custom_llm_chain/index.ts @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { KibanaRequest } from '@kbn/core/server'; +import type { PluginStartContract as ActionsPluginStart } from '@kbn/actions-plugin/server'; +import { ConversationChain } from 'langchain/chains'; +import { BufferMemory, ChatMessageHistory } from 'langchain/memory'; +import { BaseMessage } from 'langchain/schema'; + +import { ActionsClientLlm } from '../llm/actions_client_llm'; +import { ResponseBody } from '../helpers'; + +export const executeCustomLlmChain = async ({ + actions, + connectorId, + langChainMessages, + request, +}: { + actions: ActionsPluginStart; + connectorId: string; + langChainMessages: BaseMessage[]; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + request: KibanaRequest; +}): Promise => { + const llm = new ActionsClientLlm({ actions, connectorId, request }); + + const pastMessages = langChainMessages.slice(0, -1); // all but the last message + const latestMessage = langChainMessages.slice(-1); // the last message + + const memory = new BufferMemory({ + chatHistory: new ChatMessageHistory(pastMessages), + }); + + const chain = new ConversationChain({ llm, memory }); + + await chain.call({ input: latestMessage[0].content }); // kick off the chain with the last message + + // The assistant (on the client side) expects the same response returned + // from the actions framework, so we need to return the same shape of data: + const responseBody = { + connector_id: connectorId, + data: llm.getActionResultData(), // the response from the actions framework + status: 'ok', + }; + + return responseBody; +}; diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/helpers.test.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/helpers.test.ts new file mode 100644 index 0000000000000..1c62fab9df6cc --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/helpers.test.ts @@ -0,0 +1,185 @@ +/* + * 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 type { Message } from '@kbn/elastic-assistant'; +import { AIMessage, BaseMessage, HumanMessage, SystemMessage } from 'langchain/schema'; + +import { + getLangChainMessage, + getLangChainMessages, + getMessageContentAndRole, + unsafeGetAssistantMessagesFromRequest, +} from './helpers'; +import { langChainMessages } from '../../__mocks__/lang_chain_messages'; + +describe('helpers', () => { + describe('getLangChainMessage', () => { + const testCases: Array<[Pick, typeof BaseMessage]> = [ + [ + { + role: 'system', + content: 'System message', + }, + SystemMessage, + ], + [ + { + role: 'user', + content: 'User message', + }, + HumanMessage, + ], + [ + { + role: 'assistant', + content: 'Assistant message', + }, + AIMessage, + ], + [ + { + role: 'unknown' as Message['role'], + content: 'Unknown message', + }, + HumanMessage, + ], + ]; + + testCases.forEach(([testCase, expectedClass]) => { + it(`returns the expected content when role is ${testCase.role}`, () => { + const result = getLangChainMessage(testCase); + + expect(result.content).toEqual(testCase.content); + }); + + it(`returns the expected BaseMessage instance when role is ${testCase.role}`, () => { + const result = getLangChainMessage(testCase); + + expect(result instanceof expectedClass).toBeTruthy(); + }); + }); + }); + + describe('getLangChainMessages', () => { + const assistantMessages: Array> = [ + { + content: 'What is my name?', + role: 'user', + }, + { + content: + "I'm sorry, but I am not able to answer questions unrelated to Elastic Security. If you have any questions about Elastic Security, please feel free to ask.", + role: 'assistant', + }, + { + content: '\n\nMy name is Andrew', + role: 'user', + }, + { + content: + "Hello Andrew! If you have any questions about Elastic Security, feel free to ask, and I'll do my best to help you.", + role: 'assistant', + }, + { + content: '\n\nDo you know my name?', + role: 'user', + }, + ]; + + it('returns the expected BaseMessage instances', () => { + expect(getLangChainMessages(assistantMessages)).toEqual(langChainMessages); + }); + }); + + describe('getMessageContentAndRole', () => { + const testCases: Array<[string, Pick]> = [ + ['Prompt 1', { content: 'Prompt 1', role: 'user' }], + ['Prompt 2', { content: 'Prompt 2', role: 'user' }], + ['', { content: '', role: 'user' }], + ]; + + testCases.forEach(([prompt, expectedOutput]) => { + test(`Given the prompt "${prompt}", it returns the prompt as content with a "user" role`, () => { + const result = getMessageContentAndRole(prompt); + + expect(result).toEqual(expectedOutput); + }); + }); + }); + + describe('unsafeGetAssistantMessagesFromRequest', () => { + const rawSubActionParamsBody = { + messages: [ + { role: 'user', content: '\n\n\n\nWhat is my name?' }, + { + role: 'assistant', + content: + "Hello! Since we are communicating through text, I do not have the information about your name. Please feel free to share your name with me, if you'd like.", + }, + { role: 'user', content: '\n\nMy name is Andrew' }, + { + role: 'assistant', + content: + "Hi, Andrew! It's nice to meet you. How can I help you or what would you like to talk about today?", + }, + { role: 'user', content: '\n\nDo you know my name?' }, + ], + }; + + it('returns the expected assistant messages from a conversation', () => { + const result = unsafeGetAssistantMessagesFromRequest(JSON.stringify(rawSubActionParamsBody)); + + const expected = [ + { role: 'user', content: '\n\n\n\nWhat is my name?' }, + { + role: 'assistant', + content: + "Hello! Since we are communicating through text, I do not have the information about your name. Please feel free to share your name with me, if you'd like.", + }, + { role: 'user', content: '\n\nMy name is Andrew' }, + { + role: 'assistant', + content: + "Hi, Andrew! It's nice to meet you. How can I help you or what would you like to talk about today?", + }, + { role: 'user', content: '\n\nDo you know my name?' }, + ]; + + expect(result).toEqual(expected); + }); + + it('returns an empty array when the rawSubActionParamsBody is undefined', () => { + const result = unsafeGetAssistantMessagesFromRequest(undefined); + + expect(result).toEqual([]); + }); + + it('returns an empty array when the rawSubActionParamsBody messages[] array is empty', () => { + const hasEmptyMessages = { + messages: [], + }; + + const result = unsafeGetAssistantMessagesFromRequest(JSON.stringify(hasEmptyMessages)); + + expect(result).toEqual([]); + }); + + it('returns an empty array when the rawSubActionParamsBody shape is unexpected', () => { + const unexpected = { invalidKey: 'some_value' }; + + const result = unsafeGetAssistantMessagesFromRequest(JSON.stringify(unexpected)); + + expect(result).toEqual([]); + }); + + it('returns an empty array when the rawSubActionParamsBody is invalid JSON', () => { + const result = unsafeGetAssistantMessagesFromRequest('[]'); + + expect(result).toEqual([]); + }); + }); +}); diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/helpers.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/helpers.ts new file mode 100644 index 0000000000000..90364dcfe75db --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/helpers.ts @@ -0,0 +1,57 @@ +/* + * 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 type { Message } from '@kbn/elastic-assistant'; +import { AIMessage, BaseMessage, HumanMessage, SystemMessage } from 'langchain/schema'; + +export const getLangChainMessage = ( + assistantMessage: Pick +): BaseMessage => { + switch (assistantMessage.role) { + case 'system': + return new SystemMessage(assistantMessage.content); + case 'user': + return new HumanMessage(assistantMessage.content); + case 'assistant': + return new AIMessage(assistantMessage.content); + default: + return new HumanMessage(assistantMessage.content); + } +}; + +export const getLangChainMessages = ( + assistantMessages: Array> +): BaseMessage[] => assistantMessages.map(getLangChainMessage); + +export const getMessageContentAndRole = (prompt: string): Pick => ({ + content: prompt, + role: 'user', +}); + +export interface ResponseBody { + status: string; + data: Record; + connector_id: string; +} + +/** An unsafe, temporary stub that parses assistant messages from the request with no validation */ +export const unsafeGetAssistantMessagesFromRequest = ( + rawSubActionParamsBody: string | undefined +): Array> => { + try { + if (rawSubActionParamsBody == null) { + return []; + } + + const subActionParamsBody = JSON.parse(rawSubActionParamsBody); // TODO: unsafe, no validation + const messages = subActionParamsBody?.messages; + + return Array.isArray(messages) ? messages : []; + } catch { + return []; + } +}; diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/llm/actions_client_llm.test.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/llm/actions_client_llm.test.ts new file mode 100644 index 0000000000000..289793de859c0 --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/llm/actions_client_llm.test.ts @@ -0,0 +1,172 @@ +/* + * 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 { KibanaRequest } from '@kbn/core/server'; +import type { PluginStartContract as ActionsPluginStart } from '@kbn/actions-plugin/server'; + +import { ActionsClientLlm } from './actions_client_llm'; +import { mockActionResultData } from '../../../__mocks__/action_result_data'; + +const connectorId = 'mock-connector-id'; + +const mockExecute = jest.fn().mockImplementation(() => ({ + data: mockActionResultData, + status: 'ok', +})); + +const mockActions = { + getActionsClientWithRequest: jest.fn().mockImplementation(() => ({ + execute: mockExecute, + })), +} as unknown as ActionsPluginStart; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const mockRequest: KibanaRequest = { + params: { connectorId }, + body: { + params: { + subActionParams: { + body: '{"messages":[{"role":"user","content":"\\n\\n\\n\\nWhat is my name?"},{"role":"assistant","content":"I\'m sorry, but I don\'t have the information about your name. You can tell me your name if you\'d like, and we can continue our conversation from there."},{"role":"user","content":"\\n\\nMy name is Andrew"},{"role":"assistant","content":"Hello, Andrew! It\'s nice to meet you. What would you like to talk about today?"},{"role":"user","content":"\\n\\nDo you know my name?"}]}', + }, + subAction: 'test', + }, + }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any +} as KibanaRequest; + +const prompt = 'Do you know my name?'; + +describe('ActionsClientLlm', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('getActionResultData', () => { + it('returns the expected data', async () => { + const actionsClientLlm = new ActionsClientLlm({ + actions: mockActions, + connectorId, + request: mockRequest, + }); + + await actionsClientLlm._call(prompt); // ignore the result + + expect(actionsClientLlm.getActionResultData()).toEqual(mockActionResultData); + }); + }); + + describe('_llmType', () => { + it('returns the expected LLM type', () => { + const actionsClientLlm = new ActionsClientLlm({ + actions: mockActions, + connectorId, + request: mockRequest, + }); + + expect(actionsClientLlm._llmType()).toEqual('ActionsClientLlm'); + }); + }); + + describe('_call', () => { + it('returns the expected content when _call is invoked', async () => { + const actionsClientLlm = new ActionsClientLlm({ + actions: mockActions, + connectorId, + request: mockRequest, + }); + + const result = await actionsClientLlm._call(prompt); + + expect(result).toEqual('Yes, your name is Andrew. How can I assist you further, Andrew?'); + }); + + it('rejects with the expected error when the action result status is error', async () => { + const hasErrorStatus = jest.fn().mockImplementation(() => ({ + message: 'action-result-message', + serviceMessage: 'action-result-service-message', + status: 'error', // <-- error status + })); + + const badActions = { + getActionsClientWithRequest: jest.fn().mockImplementation(() => ({ + execute: hasErrorStatus, + })), + } as unknown as ActionsPluginStart; + + const actionsClientLlm = new ActionsClientLlm({ + actions: badActions, + connectorId, + request: mockRequest, + }); + + expect(actionsClientLlm._call(prompt)).rejects.toThrowError( + 'ActionsClientLlm: action result status is error: action-result-message - action-result-service-message' + ); + }); + + it('rejects with the expected error the message has invalid content', async () => { + const invalidContent = { + id: 'chatcmpl-7sFVvksgFtMUac3pY5bTypFAKaGX1', + object: 'chat.completion', + created: 1693163703, + model: 'gpt-4', + choices: [ + { + index: 0, + finish_reason: 'stop', + message: { + role: 'assistant', + content: 1234, // <-- invalid content + }, + }, + ], + usage: { completion_tokens: 16, prompt_tokens: 140, total_tokens: 156 }, + }; + + mockExecute.mockImplementation(() => ({ + data: invalidContent, + status: 'ok', + })); + + const actionsClientLlm = new ActionsClientLlm({ + actions: mockActions, + connectorId, + request: mockRequest, + }); + + expect(actionsClientLlm._call(prompt)).rejects.toThrowError( + 'ActionsClientLlm: choices[0] message content should be a string, but it had an unexpected type: number' + ); + }); + + it('rejects with the expected error when choices is empty', async () => { + const invalidContent = { + id: 'chatcmpl-7sFVvksgFtMUac3pY5bTypFAKaGX1', + object: 'chat.completion', + created: 1693163703, + model: 'gpt-4', + choices: [], // <-- empty choices + usage: { completion_tokens: 16, prompt_tokens: 140, total_tokens: 156 }, + }; + + mockExecute.mockImplementation(() => ({ + data: invalidContent, + status: 'ok', + })); + + const actionsClientLlm = new ActionsClientLlm({ + actions: mockActions, + connectorId, + request: mockRequest, + }); + + expect(actionsClientLlm._call(prompt)).rejects.toThrowError( + 'ActionsClientLlm: choices is expected to be an non-empty array' + ); + }); + }); +}); diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/llm/actions_client_llm.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/llm/actions_client_llm.ts new file mode 100644 index 0000000000000..00d78dc6cb309 --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/llm/actions_client_llm.ts @@ -0,0 +1,99 @@ +/* + * 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 { KibanaRequest } from '@kbn/core/server'; +import type { PluginStartContract as ActionsPluginStart } from '@kbn/actions-plugin/server'; +import { LLM } from 'langchain/llms/base'; +import { get } from 'lodash/fp'; + +import { getMessageContentAndRole } from '../helpers'; + +const LLM_TYPE = 'ActionsClientLlm'; + +export class ActionsClientLlm extends LLM { + #actions: ActionsPluginStart; + #connectorId: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + #request: KibanaRequest; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + #actionResultData: Record; + + constructor({ + actions, + connectorId, + request, + }: { + actions: ActionsPluginStart; + connectorId: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + request: KibanaRequest; + }) { + super({}); + + this.#actions = actions; + this.#connectorId = connectorId; + this.#request = request; + this.#actionResultData = {}; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + getActionResultData(): Record { + return this.#actionResultData; + } + + _llmType() { + return LLM_TYPE; + } + + async _call(prompt: string): Promise { + // convert the Langchain prompt to an assistant message: + const assistantMessage = getMessageContentAndRole(prompt); + + // create a new connector request body with the assistant message: + const requestBody = { + actionId: this.#connectorId, + params: { + ...this.#request.body.params, // the original request body params + subActionParams: { + ...this.#request.body.params.subActionParams, // the original request body params.subActionParams + body: JSON.stringify({ messages: [assistantMessage] }), + }, + }, + }; + + // create an actions client from the authenticated request context: + const actionsClient = await this.#actions.getActionsClientWithRequest(this.#request); + + const actionResult = await actionsClient.execute(requestBody); + + if (actionResult.status === 'error') { + throw new Error( + `${LLM_TYPE}: action result status is error: ${actionResult?.message} - ${actionResult?.serviceMessage}` + ); + } + + const choices = get('data.choices', actionResult); + + if (Array.isArray(choices) && choices.length > 0) { + // get the raw content from the first choice, because _call must return a string + const content: string | undefined = choices[0]?.message?.content; + + if (typeof content !== 'string') { + throw new Error( + `${LLM_TYPE}: choices[0] message content should be a string, but it had an unexpected type: ${typeof content}` + ); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + this.#actionResultData = actionResult.data as Record; // save the raw response from the connector, because that's what the assistant expects + + return content; // per the contact of _call, return a string + } else { + throw new Error(`${LLM_TYPE}: choices is expected to be an non-empty array`); + } + } +} diff --git a/x-pack/plugins/elastic_assistant/server/plugin.ts b/x-pack/plugins/elastic_assistant/server/plugin.ts new file mode 100755 index 0000000000000..4e277132e8da0 --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/plugin.ts @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + PluginInitializerContext, + CoreSetup, + CoreStart, + Plugin, + Logger, + IContextProvider, +} from '@kbn/core/server'; + +import { + ElasticAssistantPluginSetup, + ElasticAssistantPluginSetupDependencies, + ElasticAssistantPluginStart, + ElasticAssistantPluginStartDependencies, + ElasticAssistantRequestHandlerContext, +} from './types'; +import { postActionsConnectorExecuteRoute } from './routes'; + +export class ElasticAssistantPlugin + implements + Plugin< + ElasticAssistantPluginSetup, + ElasticAssistantPluginStart, + ElasticAssistantPluginSetupDependencies, + ElasticAssistantPluginStartDependencies + > +{ + private readonly logger: Logger; + + constructor(initializerContext: PluginInitializerContext) { + this.logger = initializerContext.logger.get(); + } + + private createRouteHandlerContext = ( + core: CoreSetup + ): IContextProvider => { + return async function elasticAssistantRouteHandlerContext(context, request) { + const [_, pluginsStart] = await core.getStartServices(); + + return { + actions: pluginsStart.actions, + }; + }; + }; + + public setup(core: CoreSetup, plugins: ElasticAssistantPluginSetupDependencies) { + this.logger.debug('elasticAssistant: Setup'); + const router = core.http.createRouter(); + + core.http.registerRouteHandlerContext< + ElasticAssistantRequestHandlerContext, + 'elasticAssistant' + >( + 'elasticAssistant', + this.createRouteHandlerContext(core as CoreSetup) + ); + + postActionsConnectorExecuteRoute(router); + return { + actions: plugins.actions, + }; + } + + public start(core: CoreStart, plugins: ElasticAssistantPluginStartDependencies) { + this.logger.debug('elasticAssistant: Started'); + + return { + actions: plugins.actions, + }; + } + + public stop() {} +} diff --git a/x-pack/plugins/elastic_assistant/server/routes/index.ts b/x-pack/plugins/elastic_assistant/server/routes/index.ts new file mode 100644 index 0000000000000..b6a53787763a0 --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/routes/index.ts @@ -0,0 +1,8 @@ +/* + * 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 { postActionsConnectorExecuteRoute } from './post_actions_connector_execute'; diff --git a/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.test.ts b/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.test.ts new file mode 100644 index 0000000000000..a5934ffb8a7a7 --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.test.ts @@ -0,0 +1,113 @@ +/* + * 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 { IRouter, KibanaRequest } from '@kbn/core/server'; +import type { PluginStartContract as ActionsPluginStart } from '@kbn/actions-plugin/server'; +import { BaseMessage } from 'langchain/schema'; + +import { mockActionResultData } from '../__mocks__/action_result_data'; +import { postActionsConnectorExecuteRoute } from './post_actions_connector_execute'; +import { ElasticAssistantRequestHandlerContext } from '../types'; + +jest.mock('../lib/build_response', () => ({ + buildResponse: jest.fn().mockImplementation((x) => x), +})); + +jest.mock('../lib/langchain/execute_custom_llm_chain', () => ({ + executeCustomLlmChain: jest.fn().mockImplementation( + async ({ + connectorId, + }: { + actions: ActionsPluginStart; + connectorId: string; + langChainMessages: BaseMessage[]; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + request: KibanaRequest; + }) => { + if (connectorId === 'mock-connector-id') { + return { + connector_id: 'mock-connector-id', + data: mockActionResultData, + status: 'ok', + }; + } else { + throw new Error('simulated error'); + } + } + ), +})); + +const mockContext = { + elasticAssistant: async () => ({ + actions: jest.fn(), + }), +}; + +const mockRequest = { + params: { connectorId: 'mock-connector-id' }, + body: { + params: { + subActionParams: { + body: '{"messages":[{"role":"user","content":"\\n\\n\\n\\nWhat is my name?"},{"role":"assistant","content":"I\'m sorry, but I don\'t have the information about your name. You can tell me your name if you\'d like, and we can continue our conversation from there."},{"role":"user","content":"\\n\\nMy name is Andrew"},{"role":"assistant","content":"Hello, Andrew! It\'s nice to meet you. What would you like to talk about today?"},{"role":"user","content":"\\n\\nDo you know my name?"}]}', + }, + subAction: 'test', + }, + }, +}; + +const mockResponse = { + ok: jest.fn().mockImplementation((x) => x), + error: jest.fn().mockImplementation((x) => x), +}; + +describe('postActionsConnectorExecuteRoute', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('returns the expected response', async () => { + const mockRouter = { + post: jest.fn().mockImplementation(async (_, handler) => { + const result = await handler(mockContext, mockRequest, mockResponse); + + expect(result).toEqual({ + body: { + connector_id: 'mock-connector-id', + data: mockActionResultData, + status: 'ok', + }, + }); + }), + }; + + await postActionsConnectorExecuteRoute( + mockRouter as unknown as IRouter + ); + }); + + it('returns the expected error when executeCustomLlmChain fails', async () => { + const requestWithBadConnectorId = { + ...mockRequest, + params: { connectorId: 'bad-connector-id' }, + }; + + const mockRouter = { + post: jest.fn().mockImplementation(async (_, handler) => { + const result = await handler(mockContext, requestWithBadConnectorId, mockResponse); + + expect(result).toEqual({ + body: 'simulated error', + statusCode: 500, + }); + }), + }; + + await postActionsConnectorExecuteRoute( + mockRouter as unknown as IRouter + ); + }); +}); diff --git a/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.ts b/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.ts new file mode 100644 index 0000000000000..be4468587bdd9 --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.ts @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IRouter } from '@kbn/core/server'; +import { transformError } from '@kbn/securitysolution-es-utils'; + +import { POST_ACTIONS_CONNECTOR_EXECUTE } from '../../common/constants'; +import { + getLangChainMessages, + unsafeGetAssistantMessagesFromRequest, +} from '../lib/langchain/helpers'; +import { buildResponse } from '../lib/build_response'; +import { buildRouteValidation } from '../schemas/common'; +import { + PostActionsConnectorExecuteBody, + PostActionsConnectorExecutePathParams, +} from '../schemas/post_actions_connector_execute'; +import { ElasticAssistantRequestHandlerContext } from '../types'; +import { executeCustomLlmChain } from '../lib/langchain/execute_custom_llm_chain'; + +export const postActionsConnectorExecuteRoute = ( + router: IRouter +) => { + router.post( + { + path: POST_ACTIONS_CONNECTOR_EXECUTE, + validate: { + body: buildRouteValidation(PostActionsConnectorExecuteBody), + params: buildRouteValidation(PostActionsConnectorExecutePathParams), + }, + }, + async (context, request, response) => { + const resp = buildResponse(response); + + try { + const connectorId = decodeURIComponent(request.params.connectorId); + const rawSubActionParamsBody = request.body.params.subActionParams.body; + + // get the actions plugin start contract from the request context: + const actions = (await context.elasticAssistant).actions; + + // get the assistant messages from the request body: + const assistantMessages = unsafeGetAssistantMessagesFromRequest(rawSubActionParamsBody); + + // convert the assistant messages to LangChain messages: + const langChainMessages = getLangChainMessages(assistantMessages); + + const langChainResponseBody = await executeCustomLlmChain({ + actions, + connectorId, + langChainMessages, + request, + }); + + return response.ok({ + body: langChainResponseBody, + }); + } catch (err) { + const error = transformError(err); + + return resp.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/elastic_assistant/server/schemas/common.ts b/x-pack/plugins/elastic_assistant/server/schemas/common.ts new file mode 100644 index 0000000000000..00e97a9326c5e --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/schemas/common.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { fold } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import type * as rt from 'io-ts'; +import { exactCheck, formatErrors } from '@kbn/securitysolution-io-ts-utils'; +import type { + RouteValidationFunction, + RouteValidationResultFactory, + RouteValidationError, +} from '@kbn/core/server'; + +type RequestValidationResult = + | { + value: T; + error?: undefined; + } + | { + value?: undefined; + error: RouteValidationError; + }; + +export const buildRouteValidation = + >(schema: T): RouteValidationFunction => + (inputValue: unknown, validationResult: RouteValidationResultFactory) => + pipe( + schema.decode(inputValue), + (decoded) => exactCheck(inputValue, decoded), + fold>( + (errors: rt.Errors) => validationResult.badRequest(formatErrors(errors).join()), + (validatedInput: A) => validationResult.ok(validatedInput) + ) + ); diff --git a/x-pack/plugins/elastic_assistant/server/schemas/post_actions_connector_execute.ts b/x-pack/plugins/elastic_assistant/server/schemas/post_actions_connector_execute.ts new file mode 100644 index 0000000000000..0aae23ed7512d --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/schemas/post_actions_connector_execute.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +/** Validates the URL path of a POST request to the `/actions/connector/{connector_id}/_execute` endpoint */ +export const PostActionsConnectorExecutePathParams = t.type({ + connectorId: t.string, +}); + +/** Validates the body of a POST request to the `/actions/connector/{connector_id}/_execute` endpoint */ +export const PostActionsConnectorExecuteBody = t.type({ + params: t.type({ + subActionParams: t.type({ + body: t.string, + }), + subAction: t.string, + }), +}); + +export type PostActionsConnectorExecuteBodyInputs = t.TypeOf< + typeof PostActionsConnectorExecuteBody +>; diff --git a/x-pack/plugins/elastic_assistant/server/types.ts b/x-pack/plugins/elastic_assistant/server/types.ts new file mode 100755 index 0000000000000..cbe7e096b4eb3 --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/types.ts @@ -0,0 +1,40 @@ +/* + * 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 type { + PluginSetupContract as ActionsPluginSetup, + PluginStartContract as ActionsPluginStart, +} from '@kbn/actions-plugin/server'; +import { CustomRequestHandlerContext } from '@kbn/core/server'; + +/** The plugin setup interface */ +export interface ElasticAssistantPluginSetup { + actions: ActionsPluginSetup; +} + +/** The plugin start interface */ +export interface ElasticAssistantPluginStart { + actions: ActionsPluginStart; +} + +export interface ElasticAssistantPluginSetupDependencies { + actions: ActionsPluginSetup; +} +export interface ElasticAssistantPluginStartDependencies { + actions: ActionsPluginStart; +} + +export interface ElasticAssistantApiRequestHandlerContext { + actions: ActionsPluginStart; +} + +/** + * @internal + */ +export type ElasticAssistantRequestHandlerContext = CustomRequestHandlerContext<{ + elasticAssistant: ElasticAssistantApiRequestHandlerContext; +}>; diff --git a/x-pack/plugins/elastic_assistant/tsconfig.json b/x-pack/plugins/elastic_assistant/tsconfig.json new file mode 100644 index 0000000000000..99119202376ec --- /dev/null +++ b/x-pack/plugins/elastic_assistant/tsconfig.json @@ -0,0 +1,27 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + }, + "include": [ + "common/**/*", + "server/lib/**/*", + "server/**/*", + // must declare *.json explicitly per https://github.com/microsoft/TypeScript/issues/25636 + "server/**/*.json", + "../../../typings/**/*" + ], + "kbn_references": [ + "@kbn/core", + "@kbn/core-http-server", + "@kbn/licensing-plugin", + "@kbn/core-http-request-handler-context-server", + "@kbn/securitysolution-es-utils", + "@kbn/securitysolution-io-ts-utils", + "@kbn/actions-plugin", + "@kbn/elastic-assistant", + ], + "exclude": [ + "target/**/*", + ] +} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/select_connector/select_connector.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/select_connector/select_connector.tsx index 1afc443077e15..83371d415ad84 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/select_connector/select_connector.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/select_connector/select_connector.tsx @@ -129,7 +129,7 @@ export const SelectConnector: React.FC = () => {

    @@ -150,7 +150,7 @@ export const SelectConnector: React.FC = () => {
    diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/constants.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/constants.ts index 950e47882af0a..94630fc054b26 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/constants.ts @@ -98,6 +98,7 @@ export const CONNECTORS_DICT: Record = { externalAuthDocsUrl: '', externalDocsUrl: '', icon: CONNECTOR_ICONS.network_drive, + platinumOnly: true, }, onedrive: { docsUrl: docLinks.connectorsOneDrive, diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/native_connector_configuration/convert_connector.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/native_connector_configuration/convert_connector.tsx index d2e009baccc79..bb1669c2c0f65 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/native_connector_configuration/convert_connector.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/native_connector_configuration/convert_connector.tsx @@ -46,7 +46,7 @@ export const ConvertConnector: React.FC = () => { {i18n.translate( 'xpack.enterpriseSearch.content.indices.configurationConnector.nativeConnector.convertConnector.title', { - defaultMessage: 'Customize your connector', + defaultMessage: 'Self-manage this connector', } )} @@ -57,7 +57,7 @@ export const ConvertConnector: React.FC = () => { diff --git a/x-pack/plugins/event_log/kibana.jsonc b/x-pack/plugins/event_log/kibana.jsonc index 1f05da271664f..ae1da1389b1eb 100644 --- a/x-pack/plugins/event_log/kibana.jsonc +++ b/x-pack/plugins/event_log/kibana.jsonc @@ -11,7 +11,8 @@ "eventLog" ], "optionalPlugins": [ - "spaces" + "spaces", + "serverless" ] } } diff --git a/x-pack/plugins/event_log/server/es/context.mock.ts b/x-pack/plugins/event_log/server/es/context.mock.ts index c974a63a6407f..90514d8f53727 100644 --- a/x-pack/plugins/event_log/server/es/context.mock.ts +++ b/x-pack/plugins/event_log/server/es/context.mock.ts @@ -20,6 +20,7 @@ const createContextMock = () => { } = { logger: loggingSystemMock.createLogger(), esNames: namesMock.create(), + shouldSetExistingAssetsToHidden: true, initialize: jest.fn(), shutdown: jest.fn(), waitTillReady: jest.fn(async () => true), diff --git a/x-pack/plugins/event_log/server/es/context.test.ts b/x-pack/plugins/event_log/server/es/context.test.ts index 681b927478d81..5cb6887af3c9b 100644 --- a/x-pack/plugins/event_log/server/es/context.test.ts +++ b/x-pack/plugins/event_log/server/es/context.test.ts @@ -34,6 +34,7 @@ describe('createEsContext', () => { test('should return is ready state as falsy if not initialized', () => { const context = createEsContext({ logger, + shouldSetExistingAssetsToHidden: true, indexNameRoot: 'test0', kibanaVersion: '1.2.3', elasticsearchClientPromise: Promise.resolve(elasticsearchClient), @@ -48,6 +49,7 @@ describe('createEsContext', () => { test('should return esNames', () => { const context = createEsContext({ logger, + shouldSetExistingAssetsToHidden: true, indexNameRoot: 'test-index', kibanaVersion: '1.2.3', elasticsearchClientPromise: Promise.resolve(elasticsearchClient), @@ -65,6 +67,7 @@ describe('createEsContext', () => { test('should return exist false for esAdapter index template and data stream before initialize', async () => { const context = createEsContext({ logger, + shouldSetExistingAssetsToHidden: true, indexNameRoot: 'test1', kibanaVersion: '1.2.3', elasticsearchClientPromise: Promise.resolve(elasticsearchClient), @@ -86,6 +89,7 @@ describe('createEsContext', () => { test('should return exist true for esAdapter index template and data stream after initialize', async () => { const context = createEsContext({ logger, + shouldSetExistingAssetsToHidden: true, indexNameRoot: 'test2', kibanaVersion: '1.2.3', elasticsearchClientPromise: Promise.resolve(elasticsearchClient), @@ -117,6 +121,7 @@ describe('createEsContext', () => { const context = createEsContext({ logger, + shouldSetExistingAssetsToHidden: true, indexNameRoot: 'test2', kibanaVersion: '1.2.3', elasticsearchClientPromise: Promise.resolve(elasticsearchClient), @@ -134,6 +139,7 @@ describe('createEsContext', () => { jest.requireMock('./init').initializeEs.mockResolvedValue(false); const context = createEsContext({ logger, + shouldSetExistingAssetsToHidden: true, indexNameRoot: 'test2', kibanaVersion: '1.2.3', elasticsearchClientPromise: Promise.resolve(elasticsearchClient), diff --git a/x-pack/plugins/event_log/server/es/context.ts b/x-pack/plugins/event_log/server/es/context.ts index c3301e321256e..25d978416e354 100644 --- a/x-pack/plugins/event_log/server/es/context.ts +++ b/x-pack/plugins/event_log/server/es/context.ts @@ -23,6 +23,7 @@ export interface EsContext { waitTillReady(): Promise; readonly initialized: boolean; readonly retryDelay: number; + shouldSetExistingAssetsToHidden: boolean; } export interface EsError { @@ -38,6 +39,7 @@ export interface EsContextCtorParams { logger: Logger; indexNameRoot: string; kibanaVersion: string; + shouldSetExistingAssetsToHidden: boolean; elasticsearchClientPromise: Promise; } @@ -48,6 +50,7 @@ class EsContextImpl implements EsContext { private readonly readySignal: ReadySignal; public initialized: boolean; public readonly retryDelay: number; + public readonly shouldSetExistingAssetsToHidden: boolean; constructor(params: EsContextCtorParams) { this.logger = params.logger; @@ -55,6 +58,7 @@ class EsContextImpl implements EsContext { this.readySignal = createReadySignal(); this.initialized = false; this.retryDelay = RETRY_DELAY; + this.shouldSetExistingAssetsToHidden = params.shouldSetExistingAssetsToHidden; this.esAdapter = new ClusterClientAdapter({ logger: params.logger, elasticsearchClientPromise: params.elasticsearchClientPromise, diff --git a/x-pack/plugins/event_log/server/es/init.test.ts b/x-pack/plugins/event_log/server/es/init.test.ts index 7c81ae80b8823..8cf2b808b8939 100644 --- a/x-pack/plugins/event_log/server/es/init.test.ts +++ b/x-pack/plugins/event_log/server/es/init.test.ts @@ -74,6 +74,12 @@ describe('initializeEs', () => { expect(esContext.esAdapter.setLegacyIndexTemplateToHidden).not.toHaveBeenCalled(); }); + test(`should not read or update existing index templates when specifying shouldSetExistingAssetsToHidden=false`, async () => { + await initializeEs({ ...esContext, shouldSetExistingAssetsToHidden: false }); + expect(esContext.esAdapter.getExistingLegacyIndexTemplates).not.toHaveBeenCalled(); + expect(esContext.esAdapter.setLegacyIndexTemplateToHidden).not.toHaveBeenCalled(); + }); + test(`should continue initialization if getting existing index templates throws an error`, async () => { esContext.esAdapter.getExistingLegacyIndexTemplates.mockRejectedValue(new Error('Fail')); @@ -198,6 +204,12 @@ describe('initializeEs', () => { expect(esContext.esAdapter.setIndexToHidden).not.toHaveBeenCalled(); }); + test(`should not read or update existing index settings when specifying shouldSetExistingAssetsToHidden=false`, async () => { + await initializeEs({ ...esContext, shouldSetExistingAssetsToHidden: false }); + expect(esContext.esAdapter.getExistingIndices).not.toHaveBeenCalled(); + expect(esContext.esAdapter.setIndexToHidden).not.toHaveBeenCalled(); + }); + test(`should continue initialization if getting existing index settings throws an error`, async () => { esContext.esAdapter.getExistingIndices.mockRejectedValue(new Error('Fail')); @@ -287,6 +299,12 @@ describe('initializeEs', () => { expect(esContext.esAdapter.setIndexAliasToHidden).not.toHaveBeenCalled(); }); + test(`should not read or update existing index aliases when specifying shouldSetExistingAssetsToHidden=false`, async () => { + await initializeEs({ ...esContext, shouldSetExistingAssetsToHidden: false }); + expect(esContext.esAdapter.getExistingIndexAliases).not.toHaveBeenCalled(); + expect(esContext.esAdapter.setIndexAliasToHidden).not.toHaveBeenCalled(); + }); + test(`should continue initialization if getting existing index aliases throws an error`, async () => { esContext.esAdapter.getExistingIndexAliases.mockRejectedValue(new Error('Fail')); diff --git a/x-pack/plugins/event_log/server/es/init.ts b/x-pack/plugins/event_log/server/es/init.ts index cf737cbf035c6..b0257f3270a55 100644 --- a/x-pack/plugins/event_log/server/es/init.ts +++ b/x-pack/plugins/event_log/server/es/init.ts @@ -31,8 +31,11 @@ export async function initializeEs(esContext: EsContext): Promise { async function initializeEsResources(esContext: EsContext) { const steps = new EsInitializationSteps(esContext); - // today, setExistingAssetsToHidden() never throws, but just in case ... - await retry(steps.setExistingAssetsToHidden); + // Only set existing assets to hidden if we're not in serverless + if (esContext.shouldSetExistingAssetsToHidden) { + // today, setExistingAssetsToHidden() never throws, but just in case ... + await retry(steps.setExistingAssetsToHidden); + } await retry(steps.createIndexTemplateIfNotExists); await retry(steps.createDataStreamIfNotExists); diff --git a/x-pack/plugins/event_log/server/plugin.test.ts b/x-pack/plugins/event_log/server/plugin.test.ts index fcfd921557645..35e02459010b1 100644 --- a/x-pack/plugins/event_log/server/plugin.test.ts +++ b/x-pack/plugins/event_log/server/plugin.test.ts @@ -18,7 +18,8 @@ describe('event_log plugin', () => { const coreStart = coreMock.createStart() as CoreStart; const plugin = new Plugin(initializerContext); - const setup = plugin.setup(coreSetup); + // serverless setup is currently empty, and there is no mock + const setup = plugin.setup(coreSetup, { serverless: {} }); expect(typeof setup.getLogger).toBe('function'); expect(typeof setup.getProviderActions).toBe('function'); expect(typeof setup.isIndexingEntries).toBe('function'); @@ -40,7 +41,8 @@ describe('event_log plugin', () => { const plugin = new Plugin(initializerContext); const spaces = spacesMock.createStart(); - plugin.setup(coreSetup); + // serverless setup is currently empty, and there is no mock + plugin.setup(coreSetup, { serverless: {} }); plugin.start(coreStart, { spaces }); await plugin.stop(); expect(mockLogger.debug).toBeCalledWith('shutdown: waiting to finish'); diff --git a/x-pack/plugins/event_log/server/plugin.ts b/x-pack/plugins/event_log/server/plugin.ts index 9c96e2ae8073a..7bd9291ab4091 100644 --- a/x-pack/plugins/event_log/server/plugin.ts +++ b/x-pack/plugins/event_log/server/plugin.ts @@ -14,6 +14,7 @@ import { IClusterClient, } from '@kbn/core/server'; import { SpacesPluginStart } from '@kbn/spaces-plugin/server'; +import { ServerlessPluginSetup } from '@kbn/serverless/server'; import type { IEventLogConfig, @@ -35,6 +36,10 @@ const ACTIONS = { stopping: 'stopping', }; +interface PluginSetupDeps { + serverless?: ServerlessPluginSetup; +} + interface PluginStartDeps { spaces?: SpacesPluginStart; } @@ -56,7 +61,7 @@ export class Plugin implements CorePlugin elasticsearch.client.asInternalUser), kibanaVersion: this.kibanaVersion, + // Only non-serverless deployments may have assets that need to be converted + shouldSetExistingAssetsToHidden: !plugins.serverless, }); this.eventLogService = new EventLogService({ diff --git a/x-pack/plugins/event_log/tsconfig.json b/x-pack/plugins/event_log/tsconfig.json index 4f69e9cce2886..cec36c8f2b785 100644 --- a/x-pack/plugins/event_log/tsconfig.json +++ b/x-pack/plugins/event_log/tsconfig.json @@ -20,6 +20,7 @@ "@kbn/utility-types", "@kbn/std", "@kbn/safer-lodash-set", + "@kbn/serverless", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/exploratory_view/public/application/index.tsx b/x-pack/plugins/exploratory_view/public/application/index.tsx index 83cf16274f9e7..e16565b3a8f36 100644 --- a/x-pack/plugins/exploratory_view/public/application/index.tsx +++ b/x-pack/plugins/exploratory_view/public/application/index.tsx @@ -97,6 +97,7 @@ export const renderApp = ({ diff --git a/x-pack/plugins/fleet/public/components/google_cloud_shell_guide.tsx b/x-pack/plugins/fleet/public/components/google_cloud_shell_guide.tsx index 89b490f2d0967..d494fc1075f41 100644 --- a/x-pack/plugins/fleet/public/components/google_cloud_shell_guide.tsx +++ b/x-pack/plugins/fleet/public/components/google_cloud_shell_guide.tsx @@ -50,10 +50,10 @@ export const GoogleCloudShellGuide = (props: { commandText: string }) => { <> - + {props.commandText} diff --git a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.helpers.ts index 4caf94db5de79..43d436c495799 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.helpers.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.helpers.ts @@ -18,11 +18,12 @@ import { IndexDetailsSection, } from '../../../public/application/sections/home/index_list/details_page'; import { WithAppDependencies } from '../helpers'; +import { testIndexName } from './mocks'; let routerMock: typeof reactRouterMock; const testBedConfig: AsyncTestBedConfig = { memoryRouter: { - initialEntries: [`/indices/test_index`], + initialEntries: [`/indices/${testIndexName}`], componentRoutePath: `/indices/:indexName/:indexDetailsSection?`, onRouter: (router) => { routerMock = router; @@ -42,6 +43,9 @@ export interface IndexDetailsPageTestBed extends TestBed { contextMenu: { clickManageIndexButton: () => Promise; isOpened: () => boolean; + clickIndexAction: (indexAction: string) => Promise; + confirmForcemerge: (numSegments: string) => Promise; + confirmDelete: () => Promise; }; errorSection: { isDisplayed: () => boolean; @@ -108,6 +112,28 @@ export const setup = async ( isOpened: () => { return exists('indexContextMenu'); }, + clickIndexAction: async (indexAction: string) => { + await act(async () => { + find(`indexContextMenu.${indexAction}`).simulate('click'); + }); + component.update(); + }, + confirmForcemerge: async (numSegments: string) => { + await act(async () => { + testBed.form.setInputValue('indexActionsForcemergeNumSegments', numSegments); + }); + component.update(); + await act(async () => { + find('confirmModalConfirmButton').simulate('click'); + }); + component.update(); + }, + confirmDelete: async () => { + await act(async () => { + find('confirmModalConfirmButton').simulate('click'); + }); + component.update(); + }, }; return { ...testBed, diff --git a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.test.ts b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.test.ts index a880933ec9fdc..7b144fd0bad40 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.test.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.test.ts @@ -9,7 +9,8 @@ import { setupEnvironment } from '../helpers'; import { IndexDetailsPageTestBed, setup } from './index_details_page.helpers'; import { act } from 'react-dom/test-utils'; import { IndexDetailsSection } from '../../../public/application/sections/home/index_list/details_page'; -import { testIndexMock } from './mocks'; +import { testIndexMock, testIndexName } from './mocks'; +import { API_BASE_PATH, INTERNAL_API_BASE_PATH } from '../../../common'; describe('', () => { let testBed: IndexDetailsPageTestBed; @@ -19,8 +20,8 @@ describe('', () => { beforeEach(async () => { const mockEnvironment = setupEnvironment(); ({ httpSetup, httpRequestsMockHelpers } = mockEnvironment); - // test_index is configured in initialEntries of the memory router - httpRequestsMockHelpers.setLoadIndexDetailsResponse('test_index', testIndexMock); + // testIndexName is configured in initialEntries of the memory router + httpRequestsMockHelpers.setLoadIndexDetailsResponse(testIndexName, testIndexMock); await act(async () => { testBed = await setup(httpSetup, { @@ -36,9 +37,9 @@ describe('', () => { describe('error section', () => { beforeEach(async () => { - httpRequestsMockHelpers.setLoadIndexDetailsResponse('test_index', undefined, { + httpRequestsMockHelpers.setLoadIndexDetailsResponse(testIndexName, undefined, { statusCode: 400, - message: 'Data for index .apm-agent-configuration was not found', + message: `Data for index ${testIndexName} was not found`, }); await act(async () => { testBed = await setup(httpSetup); @@ -59,10 +60,17 @@ describe('', () => { }); }); + it('loads index details from the API', async () => { + expect(httpSetup.get).toHaveBeenLastCalledWith( + `${INTERNAL_API_BASE_PATH}/indices/${testIndexName}`, + { asSystemRequest: undefined, body: undefined, query: undefined, version: undefined } + ); + }); + it('displays index name in the header', () => { const header = testBed.actions.getHeader(); - // test_index is configured in initialEntries of the memory router - expect(header).toEqual('test_index'); + // testIndexName is configured in initialEntries of the memory router + expect(header).toEqual(testIndexName); }); it('defaults to overview tab', () => { @@ -106,12 +114,140 @@ describe('', () => { expect(testBed.actions.discoverLinkExists()).toBe(true); }); - it('opens an index context menu when "manage index" button is clicked', async () => { - const { - actions: { contextMenu }, - } = testBed; - expect(contextMenu.isOpened()).toBe(false); - await testBed.actions.contextMenu.clickManageIndexButton(); - expect(contextMenu.isOpened()).toBe(true); + describe('context menu', () => { + it('opens an index context menu when "manage index" button is clicked', async () => { + expect(testBed.actions.contextMenu.isOpened()).toBe(false); + await testBed.actions.contextMenu.clickManageIndexButton(); + expect(testBed.actions.contextMenu.isOpened()).toBe(true); + }); + + it('closes an index', async () => { + // already sent 1 request while setting up the component + const numberOfRequests = 1; + expect(httpSetup.get).toHaveBeenCalledTimes(numberOfRequests); + + await testBed.actions.contextMenu.clickManageIndexButton(); + await testBed.actions.contextMenu.clickIndexAction('closeIndexMenuButton'); + expect(httpSetup.post).toHaveBeenCalledWith(`${API_BASE_PATH}/indices/close`, { + body: JSON.stringify({ indices: [testIndexName] }), + }); + expect(httpSetup.get).toHaveBeenCalledTimes(numberOfRequests + 1); + }); + + it('opens an index', async () => { + httpRequestsMockHelpers.setLoadIndexDetailsResponse(testIndexName, { + ...testIndexMock, + status: 'close', + }); + + await act(async () => { + testBed = await setup(httpSetup); + }); + testBed.component.update(); + + // already sent 2 requests while setting up the component + const numberOfRequests = 2; + expect(httpSetup.get).toHaveBeenCalledTimes(numberOfRequests); + + await testBed.actions.contextMenu.clickManageIndexButton(); + await testBed.actions.contextMenu.clickIndexAction('openIndexMenuButton'); + expect(httpSetup.post).toHaveBeenCalledWith(`${API_BASE_PATH}/indices/open`, { + body: JSON.stringify({ indices: [testIndexName] }), + }); + expect(httpSetup.get).toHaveBeenCalledTimes(numberOfRequests + 1); + }); + + it('forcemerges an index', async () => { + // already sent 1 request while setting up the component + const numberOfRequests = 1; + expect(httpSetup.get).toHaveBeenCalledTimes(numberOfRequests); + + await testBed.actions.contextMenu.clickManageIndexButton(); + await testBed.actions.contextMenu.clickIndexAction('forcemergeIndexMenuButton'); + await testBed.actions.contextMenu.confirmForcemerge('2'); + expect(httpSetup.post).toHaveBeenCalledWith(`${API_BASE_PATH}/indices/forcemerge`, { + body: JSON.stringify({ indices: [testIndexName], maxNumSegments: '2' }), + }); + expect(httpSetup.get).toHaveBeenCalledTimes(numberOfRequests + 1); + }); + + it('refreshes an index', async () => { + // already sent 1 request while setting up the component + const numberOfRequests = 1; + expect(httpSetup.get).toHaveBeenCalledTimes(numberOfRequests); + + await testBed.actions.contextMenu.clickManageIndexButton(); + await testBed.actions.contextMenu.clickIndexAction('refreshIndexMenuButton'); + expect(httpSetup.post).toHaveBeenCalledWith(`${API_BASE_PATH}/indices/refresh`, { + body: JSON.stringify({ indices: [testIndexName] }), + }); + expect(httpSetup.get).toHaveBeenCalledTimes(numberOfRequests + 1); + }); + + it(`clears an index's cache`, async () => { + // already sent 1 request while setting up the component + const numberOfRequests = 1; + expect(httpSetup.get).toHaveBeenCalledTimes(numberOfRequests); + + await testBed.actions.contextMenu.clickManageIndexButton(); + await testBed.actions.contextMenu.clickIndexAction('clearCacheIndexMenuButton'); + expect(httpSetup.post).toHaveBeenCalledWith(`${API_BASE_PATH}/indices/clear_cache`, { + body: JSON.stringify({ indices: [testIndexName] }), + }); + expect(httpSetup.get).toHaveBeenCalledTimes(numberOfRequests + 1); + }); + + it(`flushes an index`, async () => { + // already sent 1 request while setting up the component + const numberOfRequests = 1; + expect(httpSetup.get).toHaveBeenCalledTimes(numberOfRequests); + + await testBed.actions.contextMenu.clickManageIndexButton(); + await testBed.actions.contextMenu.clickIndexAction('flushIndexMenuButton'); + expect(httpSetup.post).toHaveBeenCalledWith(`${API_BASE_PATH}/indices/flush`, { + body: JSON.stringify({ indices: [testIndexName] }), + }); + expect(httpSetup.get).toHaveBeenCalledTimes(numberOfRequests + 1); + }); + + it(`deletes an index`, async () => { + jest.spyOn(testBed.routerMock.history, 'push'); + // already sent 1 request while setting up the component + const numberOfRequests = 1; + expect(httpSetup.get).toHaveBeenCalledTimes(numberOfRequests); + + await testBed.actions.contextMenu.clickManageIndexButton(); + await testBed.actions.contextMenu.clickIndexAction('deleteIndexMenuButton'); + await testBed.actions.contextMenu.confirmDelete(); + expect(httpSetup.post).toHaveBeenCalledWith(`${API_BASE_PATH}/indices/delete`, { + body: JSON.stringify({ indices: [testIndexName] }), + }); + + expect(testBed.routerMock.history.push).toHaveBeenCalledTimes(1); + expect(testBed.routerMock.history.push).toHaveBeenCalledWith('/indices'); + }); + + it(`unfreezes a frozen index`, async () => { + httpRequestsMockHelpers.setLoadIndexDetailsResponse(testIndexName, { + ...testIndexMock, + isFrozen: true, + }); + + await act(async () => { + testBed = await setup(httpSetup); + }); + testBed.component.update(); + + // already sent 1 request while setting up the component + const numberOfRequests = 2; + expect(httpSetup.get).toHaveBeenCalledTimes(numberOfRequests); + + await testBed.actions.contextMenu.clickManageIndexButton(); + await testBed.actions.contextMenu.clickIndexAction('unfreezeIndexMenuButton'); + expect(httpSetup.post).toHaveBeenCalledWith(`${API_BASE_PATH}/indices/unfreeze`, { + body: JSON.stringify({ indices: [testIndexName] }), + }); + expect(httpSetup.get).toHaveBeenCalledTimes(numberOfRequests + 1); + }); }); }); diff --git a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/mocks.ts b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/mocks.ts index d96f1d0fac0dc..5e165fe0702e6 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/mocks.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/mocks.ts @@ -7,10 +7,11 @@ import { Index } from '../../../public'; +export const testIndexName = 'test_index'; export const testIndexMock: Index = { health: 'green', status: 'open', - name: 'test_index', + name: testIndexName, uuid: 'test1234', primary: '1', replica: '1', @@ -21,7 +22,6 @@ export const testIndexMock: Index = { isFrozen: false, aliases: 'none', hidden: false, - // @ts-expect-error ts upgrade v4.7.4 isRollupIndex: false, ilm: { index: 'test_index', diff --git a/x-pack/plugins/index_management/common/types/indices.ts b/x-pack/plugins/index_management/common/types/indices.ts index ab54812f2f0e3..608ee392a3f9e 100644 --- a/x-pack/plugins/index_management/common/types/indices.ts +++ b/x-pack/plugins/index_management/common/types/indices.ts @@ -64,6 +64,15 @@ export interface Index { hidden: boolean; aliases: string | string[]; data_stream?: string; + + // The types below are added by extension services if corresponding plugins are enabled (ILM, Rollup, CCR) + isRollupIndex?: boolean; + ilm?: { + index: string; + managed: boolean; + }; + isFollowerIndex?: boolean; + // The types from here below represent information returned from the index stats API; // treated optional as the stats API is not available on serverless health?: HealthStatus; diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page.tsx index 573bd1d7ee353..6d91b3a7991a1 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { Redirect, RouteComponentProps } from 'react-router-dom'; import { Route, Routes } from '@kbn/shared-ux-router'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -17,11 +17,14 @@ import { EuiButton, } from '@elastic/eui'; import { SectionLoading } from '@kbn/es-ui-shared-plugin/public'; + +import { Index } from '../../../../../../common'; +import { loadIndex } from '../../../../services'; import { DiscoverLink } from '../../../../lib/discover_link'; -import { useLoadIndex } from '../../../../services'; import { Section } from '../../home'; import { DetailsPageError } from './details_page_error'; -import { IndexActionsContextMenuWithoutRedux } from '../index_actions_context_menu/index_actions_context_menu.without_redux'; +import { ManageIndexButton } from './manage_index_button'; + export enum IndexDetailsSection { Overview = 'overview', Documents = 'documents', @@ -69,6 +72,27 @@ export const DetailsPage: React.FunctionComponent< }, history, }) => { + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(); + const [index, setIndex] = useState(); + + const fetchIndexDetails = useCallback(async () => { + setIsLoading(true); + try { + const { data, error: loadingError } = await loadIndex(indexName); + setIsLoading(false); + setError(loadingError); + setIndex(data); + } catch (e) { + setIsLoading(false); + setError(e); + } + }, [indexName]); + + useEffect(() => { + fetchIndexDetails(); + }, [fetchIndexDetails]); + const onSectionChange = useCallback( (newSection: IndexDetailsSection) => { return history.push(encodeURI(`/indices/${indexName}/${newSection}`)); @@ -76,6 +100,10 @@ export const DetailsPage: React.FunctionComponent< [history, indexName] ); + const navigateToAllIndices = useCallback(() => { + history.push(`/${Section.Indices}`); + }, [history]); + const headerTabs = useMemo(() => { return tabs.map((tab) => ({ onClick: () => onSectionChange(tab.id), @@ -86,8 +114,7 @@ export const DetailsPage: React.FunctionComponent< })); }, [indexDetailsSection, onSectionChange]); - const { isLoading, error, resendRequest, data } = useLoadIndex(indexName); - if (isLoading) { + if (isLoading && !index) { return ( ); } - if (error || !data) { - return ; + if (error || !index) { + return ; } return ( @@ -108,9 +135,7 @@ export const DetailsPage: React.FunctionComponent< data-test-subj="indexDetailsBackToIndicesButton" color="text" iconType="arrowLeft" - onClick={() => { - return history.push(`/${Section.Indices}`); - }} + onClick={navigateToAllIndices} > , - , ]} tabs={headerTabs} @@ -166,6 +192,11 @@ export const DetailsPage: React.FunctionComponent< />

    + + +
    +
    {JSON.stringify(index, null, 2)}
    +
    ); }; diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_error.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_error.tsx index 6defc3d3db034..c1159cecfe09f 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_error.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_error.tsx @@ -8,14 +8,13 @@ import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiButton, EuiPageTemplate, EuiSpacer, EuiText } from '@elastic/eui'; -import { useLoadIndex } from '../../../../services'; export const DetailsPageError = ({ indexName, resendRequest, }: { indexName: string; - resendRequest: ReturnType['resendRequest']; + resendRequest: () => Promise; }) => { return ( { + const indexStatusByName: IndexActionsContextMenuProps['indexStatusByName'] = {}; + indexNames.forEach((indexName) => { + const { status } = indices.find((index) => index.name === indexName) ?? {}; + indexStatusByName[indexName] = status; + }); + return indexStatusByName; +}; + +interface Props { + indexName: string; + indexDetails: Index; + reloadIndexDetails: () => Promise; + navigateToAllIndices: () => void; +} +export const ManageIndexButton: FunctionComponent = ({ + indexName, + indexDetails, + reloadIndexDetails, + navigateToAllIndices, +}) => { + const [isLoading, setIsLoading] = useState(false); + + // the variables are created to write the index actions in a way to later re-use for indices list without redux + const indexNames = useMemo(() => [indexName], [indexName]); + + const reloadIndices = useCallback(async () => { + setIsLoading(true); + await reloadIndexDetails(); + setIsLoading(false); + }, [reloadIndexDetails]); + + const indices = [indexDetails]; + const indexStatusByName = getIndexStatusByName(indexNames, indices); + + const closeIndices = useCallback(async () => { + setIsLoading(true); + try { + await closeIndicesRequest(indexNames); + await reloadIndices(); + setIsLoading(false); + notificationService.showSuccessToast( + i18n.translate('xpack.idxMgmt.closeIndicesAction.successfullyClosedIndicesMessage', { + defaultMessage: 'Successfully closed: [{indexNames}]', + values: { indexNames: indexNames.join(', ') }, + }) + ); + } catch (error) { + setIsLoading(false); + notificationService.showDangerToast(error.body.message); + } + }, [reloadIndices, indexNames]); + + const openIndices = useCallback(async () => { + setIsLoading(true); + try { + await openIndicesRequest(indexNames); + await reloadIndices(); + setIsLoading(false); + notificationService.showSuccessToast( + i18n.translate('xpack.idxMgmt.openIndicesAction.successfullyOpenedIndicesMessage', { + defaultMessage: 'Successfully opened: [{indexNames}]', + values: { indexNames: indexNames.join(', ') }, + }) + ); + } catch (error) { + setIsLoading(false); + notificationService.showDangerToast(error.body.message); + } + }, [reloadIndices, indexNames]); + + const flushIndices = useCallback(async () => { + setIsLoading(true); + try { + await flushIndicesRequest(indexNames); + await reloadIndices(); + setIsLoading(false); + notificationService.showSuccessToast( + i18n.translate('xpack.idxMgmt.flushIndicesAction.successfullyFlushedIndicesMessage', { + defaultMessage: 'Successfully flushed: [{indexNames}]', + values: { indexNames: indexNames.join(', ') }, + }) + ); + } catch (error) { + setIsLoading(false); + notificationService.showDangerToast(error.body.message); + } + }, [reloadIndices, indexNames]); + + const refreshIndices = useCallback(async () => { + setIsLoading(true); + try { + await refreshIndicesRequest(indexNames); + await reloadIndices(); + setIsLoading(false); + notificationService.showSuccessToast( + i18n.translate('xpack.idxMgmt.refreshIndicesAction.successfullyRefreshedIndicesMessage', { + defaultMessage: 'Successfully refreshed: [{indexNames}]', + values: { indexNames: indexNames.join(', ') }, + }) + ); + } catch (error) { + setIsLoading(false); + notificationService.showDangerToast(error.body.message); + } + }, [reloadIndices, indexNames]); + + const clearCacheIndices = useCallback(async () => { + setIsLoading(true); + try { + await clearCacheIndicesRequest(indexNames); + await reloadIndices(); + setIsLoading(false); + notificationService.showSuccessToast( + i18n.translate('xpack.idxMgmt.clearCacheIndicesAction.successMessage', { + defaultMessage: 'Successfully cleared cache: [{indexNames}]', + values: { indexNames: indexNames.join(', ') }, + }) + ); + } catch (error) { + setIsLoading(false); + notificationService.showDangerToast(error.body.message); + } + }, [reloadIndices, indexNames]); + + const unfreezeIndices = useCallback(async () => { + setIsLoading(true); + try { + await unfreezeIndicesRequest(indexNames); + await reloadIndices(); + setIsLoading(false); + notificationService.showSuccessToast( + i18n.translate('xpack.idxMgmt.unfreezeIndicesAction.successfullyUnfrozeIndicesMessage', { + defaultMessage: 'Successfully unfroze: [{indexNames}]', + values: { indexNames: indexNames.join(', ') }, + }) + ); + } catch (error) { + setIsLoading(false); + notificationService.showDangerToast(error.body.message); + } + }, [reloadIndices, indexNames]); + + const forcemergeIndices = useCallback( + async (maxNumSegments: string) => { + setIsLoading(true); + try { + await forcemergeIndicesRequest(indexNames, maxNumSegments); + await reloadIndices(); + setIsLoading(false); + notificationService.showSuccessToast( + i18n.translate( + 'xpack.idxMgmt.forceMergeIndicesAction.successfullyForceMergedIndicesMessage', + { + defaultMessage: 'Successfully force merged: [{indexNames}]', + values: { indexNames: indexNames.join(', ') }, + } + ) + ); + } catch (error) { + setIsLoading(false); + notificationService.showDangerToast(error.body.message); + } + }, + [reloadIndices, indexNames] + ); + + const deleteIndices = useCallback(async () => { + setIsLoading(true); + try { + await deleteIndicesRequest(indexNames); + setIsLoading(false); + notificationService.showSuccessToast( + i18n.translate('xpack.idxMgmt.deleteIndicesAction.successfullyDeletedIndicesMessage', { + defaultMessage: 'Successfully deleted: [{indexNames}]', + values: { indexNames: indexNames.join(', ') }, + }) + ); + navigateToAllIndices(); + } catch (error) { + setIsLoading(false); + notificationService.showDangerToast(error.body.message); + } + }, [navigateToAllIndices, indexNames]); + + const performExtensionAction = useCallback( + async ( + requestMethod: (indexNames: string[], http: HttpSetup) => Promise, + successMessage: string + ) => { + setIsLoading(true); + try { + await requestMethod(indexNames, httpService.httpClient); + await reloadIndices(); + setIsLoading(false); + notificationService.showSuccessToast(successMessage); + } catch (error) { + setIsLoading(false); + notificationService.showDangerToast(error.body.message); + } + }, + [reloadIndices, indexNames] + ); + + return ( + + ); +}; diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.d.ts b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.d.ts new file mode 100644 index 0000000000000..896622ac0e9fb --- /dev/null +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.d.ts @@ -0,0 +1,68 @@ +/* + * 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 type { ClassComponent, Component } from 'react'; +import type { HttpSetup } from '@kbn/core-http-browser'; +import type { EuiPopoverProps, EuiButtonProps } from '@elastic/eui'; +import type { Index } from '../../../../../../common'; + +export interface IndexActionsContextMenuProps { + // either an array of indices selected in the list view or an array of 1 index name on the details panel/page + indexNames: string[]; + // indices data + indices: Index[]; + + // indicates if the context menu is on the list view (to show additional actions) + isOnListView?: boolean; + // a callback used to reset selected indices on the list view + resetSelection?: () => void; + + // these props are only set on the details panel to change style + anchorPosition?: EuiPopoverProps['anchorPosition']; + iconSide?: EuiButtonProps['iconSide']; + iconType?: EuiButtonProps['iconType']; + label?: Component; + + // index actions: functions are called with indexNames prop so no need to pass it as argument here + closeIndices: () => Promise; + openIndices: () => Promise; + flushIndices: () => Promise; + refreshIndices: () => Promise; + clearCacheIndices: () => Promise; + unfreezeIndices: () => Promise; + forcemergeIndices: (maxNumSegments: string) => Promise; + deleteIndices: () => Promise; + + // following 4 actions are only added when on the list view and only 1 index is selected + showSettings?: () => void; // opens the settings tab for the 1st index in the indexNames array + showMapping?: () => void; // opens the mapping tab for the 1st index in the indexNames array + showStats?: () => void; // opens the stats tab for the 1st index in the indexNames array + editIndex?: () => void; // opens the edit settings tab for the 1st index in the indexNames array + + // used to determine if all indices are open + indexStatusByName: { + [indexName: string]: Index['status'] | undefined; + }; + + // this function is called with an extension service action + performExtensionAction: ( + requestMethod: (indexNames: string[], http: HttpSetup) => Promise, + successMessage: string + ) => Promise; + // this function is called to "refresh" the indices data after and extension service action that uses a modal + reloadIndices: () => void; + + /** + * Props added to use the context menu on the new index details page + */ + // makes the button secondary + fill?: boolean; + // sets the button's loading state + isLoading?: boolean; +} + +export const IndexActionsContextMenu: ClassComponent; diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.js index 8fb7fa5410f72..54ba5dee8250e 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.js +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.js @@ -198,6 +198,7 @@ export class IndexActionsContextMenu extends Component { }); } items.push({ + 'data-test-subj': 'deleteIndexMenuButton', name: i18n.translate('xpack.idxMgmt.indexActionsMenu.deleteIndexLabel', { defaultMessage: 'Delete {selectedIndexCount, plural, one {index} other {indices} }', values: { selectedIndexCount }, @@ -372,6 +373,7 @@ export class IndexActionsContextMenu extends Component { helpText={helpText} > { this.setState({ forcemergeSegments: event.target.value }); }} @@ -464,6 +466,7 @@ export class IndexActionsContextMenu extends Component { }), iconType = 'arrowDown', fill = true, + isLoading = false, } = this.props; const panels = this.panels(appDependencies); @@ -480,6 +483,7 @@ export class IndexActionsContextMenu extends Component { onClick={this.onButtonClick} iconType={iconType} fill={fill} + isLoading={isLoading} > {label} diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.without_redux.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.without_redux.tsx deleted file mode 100644 index 177bfc7350af0..0000000000000 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.without_redux.tsx +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { FunctionComponent } from 'react'; -import { HttpSetup } from '@kbn/core-http-browser'; -import { EuiButtonProps } from '@elastic/eui/src/components/button/button'; -import { EuiPopoverProps } from '@elastic/eui/src/components/popover/popover'; -import { Index } from '../../../../../../common'; -import { reloadIndices } from '../../../../services'; -// @ts-ignore this component needs to be refactored into TS -import { IndexActionsContextMenu } from './index_actions_context_menu'; - -export interface ReduxProps { - closeIndices: ({}: { indexNames: string[] }) => Promise; - openIndices: ({}: { indexNames: string[] }) => Promise; - flushIndices: ({}: { indexNames: string[] }) => Promise; - refreshIndices: ({}: { indexNames: string[] }) => Promise; - clearCacheIndices: ({}: { indexNames: string[] }) => Promise; - unfreezeIndices: ({}: { indexNames: string[] }) => Promise; - forcemergeIndices: ({}: { indexNames: string[]; maxNumSegments: number }) => Promise; - deleteIndices: ({}: { indexNames: string[] }) => Promise; - - // following 4 actions are only added when on the list view and only 1 index is selected - showSettings: ({}: { indexNames: string[] }) => void; // opens the settings tab for the 1st index - showMapping: ({}: { indexNames: string[] }) => void; // opens the mapping tab for the 1st index - showStats: ({}: { indexNames: string[] }) => void; // opens the stats tab for the 1st index - editIndex: ({}: { indexNames: string[] }) => void; // opens the edit settings tab for the 1st index - - indexStatusByName: { - [indexName: string]: Index['status'] | undefined; - }; - reloadIndices: typeof reloadIndices; - - // this comes from the extension service - performExtensionAction: ({}: { - requestMethod: (indexNames: string[], httpClient: HttpSetup) => Promise; - indexNames: string[]; - successMessage: string; - }) => Promise; -} - -interface Props { - // either an array of indices selected in the list view or an array of 1 index name on details panel/page - indexNames: string[]; - - // indicates if the context menu is on the list view (to show additional actions) - isOnListView?: boolean; - // a callback used to reset selected indices on the list view - resetSelection?: () => void; - - // these props are only set on the details panel to change style - anchorPosition?: EuiPopoverProps['anchorPosition']; - iconSide?: EuiButtonProps['iconSide']; - iconType?: EuiButtonProps['iconType']; - label?: React.Component; - - // a new prop to make the button secondary - fill?: boolean; - - // instead of getting indices data from the redux store, pass it as a prop - indices: Index[]; -} - -const getIndexStatusByName = ( - indexNames: string[], - indices: Index[] -): ReduxProps['indexStatusByName'] => { - const indexStatusByName: ReduxProps['indexStatusByName'] = {}; - indexNames.forEach((indexName) => { - const { status } = indices.find((index) => index.name === indexName) ?? {}; - indexStatusByName[indexName] = status; - }); - return indexStatusByName; -}; - -export const IndexActionsContextMenuWithoutRedux: FunctionComponent = ({ - indexNames, - indices, - ...rest -}) => { - const props: ReduxProps = { - closeIndices: async () => {}, - openIndices: async () => {}, - flushIndices: async () => {}, - refreshIndices: async () => {}, - clearCacheIndices: async () => {}, - unfreezeIndices: async () => {}, - forcemergeIndices: async () => {}, - deleteIndices: async () => {}, - - // there actions are not displayed on the index details page - showSettings: () => {}, - showMapping: () => {}, - showStats: () => {}, - editIndex: () => {}, - - indexStatusByName: getIndexStatusByName(indexNames, indices), - reloadIndices: async () => {}, - - performExtensionAction: async () => {}, - }; - return ; -}; diff --git a/x-pack/plugins/index_management/public/application/services/api.ts b/x-pack/plugins/index_management/public/application/services/api.ts index 5defc62ece088..b19382364722c 100644 --- a/x-pack/plugins/index_management/public/application/services/api.ts +++ b/x-pack/plugins/index_management/public/application/services/api.ts @@ -31,8 +31,9 @@ import { UIM_TEMPLATE_UPDATE, UIM_TEMPLATE_CLONE, UIM_TEMPLATE_SIMULATE, + INTERNAL_API_BASE_PATH, } from '../../../common/constants'; -import { TemplateDeserialized, TemplateListItem, DataStream } from '../../../common'; +import { TemplateDeserialized, TemplateListItem, DataStream, Index } from '../../../common'; import { TAB_SETTINGS, TAB_MAPPING, TAB_STATS } from '../constants'; import { useRequest, sendRequest } from './use_request'; import { httpService } from './http'; @@ -311,3 +312,10 @@ export function useLoadNodesPlugins() { method: 'get', }); } + +export function loadIndex(indexName: string) { + return sendRequest({ + path: `${INTERNAL_API_BASE_PATH}/indices/${indexName}`, + method: 'get', + }); +} diff --git a/x-pack/plugins/index_management/public/application/services/index.ts b/x-pack/plugins/index_management/public/application/services/index.ts index 96e961b718bd9..5cb65c04b6c9d 100644 --- a/x-pack/plugins/index_management/public/application/services/index.ts +++ b/x-pack/plugins/index_management/public/application/services/index.ts @@ -24,10 +24,9 @@ export { useLoadIndexTemplates, simulateIndexTemplate, useLoadNodesPlugins, + loadIndex, } from './api'; -export { useLoadIndex } from './indices_api'; - export { sortTable } from './sort_table'; export { UiMetricService } from './ui_metric'; diff --git a/x-pack/plugins/index_management/public/application/services/indices_api.ts b/x-pack/plugins/index_management/public/application/services/indices_api.ts deleted file mode 100644 index c7e4cc5775ce5..0000000000000 --- a/x-pack/plugins/index_management/public/application/services/indices_api.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { useRequest } from './use_request'; -import { Index, INTERNAL_API_BASE_PATH } from '../../../common'; - -export const useLoadIndex = (indexName: string) => { - return useRequest({ - path: `${INTERNAL_API_BASE_PATH}/indices/${indexName}`, - method: 'get', - }); -}; diff --git a/x-pack/plugins/index_management/public/application/services/use_request.ts b/x-pack/plugins/index_management/public/application/services/use_request.ts index 3b1d5cf22452d..4746890361d59 100644 --- a/x-pack/plugins/index_management/public/application/services/use_request.ts +++ b/x-pack/plugins/index_management/public/application/services/use_request.ts @@ -16,7 +16,9 @@ import { import { httpService } from './http'; -export const sendRequest = (config: SendRequestConfig): Promise => { +export const sendRequest = ( + config: SendRequestConfig +): Promise> => { return _sendRequest(httpService.httpClient, config); }; diff --git a/x-pack/plugins/infra/public/common/visualizations/constants.ts b/x-pack/plugins/infra/public/common/visualizations/constants.ts index 1ef3484843507..499dc6f212850 100644 --- a/x-pack/plugins/infra/public/common/visualizations/constants.ts +++ b/x-pack/plugins/infra/public/common/visualizations/constants.ts @@ -7,6 +7,13 @@ import { cpuUsage, + cpuUsageIowait, + cpuUsageIrq, + cpuUsageNice, + cpuUsageSoftirq, + cpuUsageSteal, + cpuUsageUser, + cpuUsageSystem, diskIORead, diskIOWrite, diskReadThroughput, @@ -16,8 +23,14 @@ import { diskSpaceUsage, logRate, normalizedLoad1m, + load1m, + load5m, + load15m, memoryUsage, memoryFree, + memoryUsed, + memoryFreeExcludingCache, + memoryCache, rx, tx, hostCount, @@ -25,6 +38,13 @@ import { export const hostLensFormulas = { cpuUsage, + cpuUsageIowait, + cpuUsageIrq, + cpuUsageNice, + cpuUsageSoftirq, + cpuUsageSteal, + cpuUsageUser, + cpuUsageSystem, diskIORead, diskIOWrite, diskReadThroughput, @@ -35,8 +55,14 @@ export const hostLensFormulas = { hostCount, logRate, normalizedLoad1m, + load1m, + load5m, + load15m, memoryUsage, memoryFree, + memoryUsed, + memoryFreeExcludingCache, + memoryCache, rx, tx, }; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/host/host_metric_charts.ts b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/host/host_metric_charts.ts index e864044c928ad..0c31e1f2e6643 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/host/host_metric_charts.ts +++ b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/host/host_metric_charts.ts @@ -4,214 +4,40 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { cpuUsage, normalizedLoad1m, cpuUsageBreakdown, loadBreakdown } from '../metric_charts/cpu'; +import { + diskSpaceUsageAvailable, + diskThroughputReadWrite, + diskIOReadWrite, + diskSpaceUsageByMountPoint, +} from '../metric_charts/disk'; +import { logRate } from '../metric_charts/log'; +import { memoryUsage, memoryUsageBreakdown } from '../metric_charts/memory'; +import { rxTx } from '../metric_charts/network'; +import type { XYConfig } from '../metric_charts/types'; -import { i18n } from '@kbn/i18n'; -import type { TypedLensByValueInput } from '@kbn/lens-plugin/public'; -import { hostLensFormulas } from '../../../../constants'; -import type { XYChartLayerParams } from '../../../../types'; -import { REFERENCE_LINE, XY_OVERRIDES } from '../../constants'; - -type DataViewOrigin = 'logs' | 'metrics'; - -export const hostMetricCharts: Array< - Pick & { - dataViewOrigin: DataViewOrigin; - layers: XYChartLayerParams[]; - } -> = [ - { - id: 'cpuUsage', - title: i18n.translate('xpack.infra.assetDetails.metricsCharts.cpuUsage', { - defaultMessage: 'CPU Usage', - }), +export const hostMetricCharts: XYConfig[] = [ + cpuUsage, + memoryUsage, + normalizedLoad1m, + logRate, + diskSpaceUsageAvailable, + diskThroughputReadWrite, + diskIOReadWrite, + rxTx, +]; - layers: [ - { - data: [hostLensFormulas.cpuUsage], - type: 'visualization', - }, - ], - dataViewOrigin: 'metrics', - overrides: { - axisLeft: XY_OVERRIDES.axisLeft, - }, - }, - { - id: 'memoryUsage', - title: i18n.translate('xpack.infra.assetDetails.metricsCharts.memoryUsage', { - defaultMessage: 'Memory Usage', - }), - layers: [ - { - data: [hostLensFormulas.memoryUsage], - type: 'visualization', - }, - ], - dataViewOrigin: 'metrics', - overrides: { - axisLeft: XY_OVERRIDES.axisLeft, - }, - }, - { - id: 'normalizedLoad1m', - title: i18n.translate('xpack.infra.assetDetails.metricsCharts.normalizedLoad1m', { - defaultMessage: 'Normalized Load', - }), - layers: [ - { - data: [hostLensFormulas.normalizedLoad1m], - type: 'visualization', - }, - { - data: [REFERENCE_LINE], - type: 'referenceLines', - }, - ], - dataViewOrigin: 'metrics', - }, - { - id: 'logRate', - title: i18n.translate('xpack.infra.assetDetails.metricsCharts.logRate', { - defaultMessage: 'Log Rate', - }), - layers: [ - { - data: [hostLensFormulas.logRate], - type: 'visualization', - }, - ], - dataViewOrigin: 'logs', - }, - { - id: 'diskSpaceUsageAvailable', - title: i18n.translate('xpack.infra.assetDetails.metricsCharts.diskSpace', { - defaultMessage: 'Disk Space', - }), - layers: [ - { - data: [ - { - ...hostLensFormulas.diskSpaceUsage, - label: i18n.translate('xpack.infra.assetDetails.metricsCharts.diskSpace.label.used', { - defaultMessage: 'Used', - }), - }, - { - ...hostLensFormulas.diskSpaceAvailability, - label: i18n.translate( - 'xpack.infra.assetDetails.metricsCharts.diskSpace.label.available', - { - defaultMessage: 'Available', - } - ), - }, - ], - options: { - seriesType: 'area', - }, - type: 'visualization', - }, - ], - overrides: { - axisLeft: XY_OVERRIDES.axisLeft, - settings: XY_OVERRIDES.settings, - }, - dataViewOrigin: 'metrics', - }, - { - id: 'diskThroughputReadWrite', - title: i18n.translate('xpack.infra.assetDetails.metricsCharts.diskIOPS', { - defaultMessage: 'Disk IOPS', - }), - layers: [ - { - data: [ - { - ...hostLensFormulas.diskIORead, - label: i18n.translate('xpack.infra.assetDetails.metricsCharts.metric.label.read', { - defaultMessage: 'Read', - }), - }, - { - ...hostLensFormulas.diskIOWrite, - label: i18n.translate('xpack.infra.assetDetails.metricsCharts.metric.label.write', { - defaultMessage: 'Write', - }), - }, - ], - options: { - seriesType: 'area', - }, - type: 'visualization', - }, - ], - overrides: { - settings: XY_OVERRIDES.settings, - }, - dataViewOrigin: 'metrics', - }, - { - id: 'diskIOReadWrite', - title: i18n.translate('xpack.infra.assetDetails.metricsCharts.diskThroughput', { - defaultMessage: 'Disk Throughput', - }), - layers: [ - { - data: [ - { - ...hostLensFormulas.diskReadThroughput, - label: i18n.translate('xpack.infra.assetDetails.metricsCharts.metric.label.read', { - defaultMessage: 'Read', - }), - }, - { - ...hostLensFormulas.diskWriteThroughput, - label: i18n.translate('xpack.infra.assetDetails.metricsCharts.metric.label.write', { - defaultMessage: 'Write', - }), - }, - ], - options: { - seriesType: 'area', - }, - type: 'visualization', - }, - ], - overrides: { - settings: XY_OVERRIDES.settings, - }, - dataViewOrigin: 'metrics', - }, - { - id: 'rxTx', - title: i18n.translate('xpack.infra.assetDetails.metricsCharts.network', { - defaultMessage: 'Network', - }), - layers: [ - { - data: [ - { - ...hostLensFormulas.rx, - label: i18n.translate('xpack.infra.assetDetails.metricsCharts.network.label.rx', { - defaultMessage: 'Inbound (RX)', - }), - }, - { - ...hostLensFormulas.tx, - label: i18n.translate('xpack.infra.assetDetails.metricsCharts.network.label.tx', { - defaultMessage: 'Outbound (TX)', - }), - }, - ], - options: { - seriesType: 'area', - }, - type: 'visualization', - }, - ], - overrides: { - settings: XY_OVERRIDES.settings, - }, - dataViewOrigin: 'metrics', - }, +export const hostMetricChartsFullPage: XYConfig[] = [ + cpuUsage, + cpuUsageBreakdown, + memoryUsage, + memoryUsageBreakdown, + normalizedLoad1m, + loadBreakdown, + logRate, + diskSpaceUsageAvailable, + diskSpaceUsageByMountPoint, + diskThroughputReadWrite, + diskIOReadWrite, + rxTx, ]; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/index.ts b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/index.ts index aacc3876980e4..4222b145e2552 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/index.ts +++ b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/index.ts @@ -5,10 +5,10 @@ * 2.0. */ -import { hostMetricCharts } from './host/host_metric_charts'; +import { hostMetricCharts, hostMetricChartsFullPage } from './host/host_metric_charts'; import { hostKPICharts, KPIChartProps } from './host/host_kpi_charts'; export { type KPIChartProps }; export const assetDetailsDashboards = { - host: { hostMetricCharts, hostKPICharts }, + host: { hostMetricCharts, hostMetricChartsFullPage, hostKPICharts }, }; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/cpu.ts b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/cpu.ts new file mode 100644 index 0000000000000..6e9c53cce635e --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/cpu.ts @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { hostLensFormulas } from '../../../../constants'; +import { REFERENCE_LINE, XY_OVERRIDES } from '../../constants'; +import type { XYConfig } from './types'; + +export const cpuUsage: XYConfig = { + id: 'cpuUsage', + title: i18n.translate('xpack.infra.assetDetails.metricsCharts.cpuUsage', { + defaultMessage: 'CPU Usage', + }), + + layers: [ + { + data: [hostLensFormulas.cpuUsage], + type: 'visualization', + }, + ], + dataViewOrigin: 'metrics', + overrides: { + axisLeft: XY_OVERRIDES.axisLeft, + }, +}; + +export const cpuUsageBreakdown: XYConfig = { + id: 'cpuUsageBreakdown', + title: i18n.translate('xpack.infra.assetDetails.metricsCharts.cpuUsage', { + defaultMessage: 'CPU Usage', + }), + layers: [ + { + data: [ + hostLensFormulas.cpuUsageIowait, + hostLensFormulas.cpuUsageIrq, + hostLensFormulas.cpuUsageNice, + hostLensFormulas.cpuUsageSoftirq, + hostLensFormulas.cpuUsageSteal, + hostLensFormulas.cpuUsageUser, + hostLensFormulas.cpuUsageSystem, + ], + options: { + seriesType: 'area_percentage_stacked', + }, + type: 'visualization', + }, + ], + overrides: { + axisLeft: XY_OVERRIDES.axisLeft, + settings: XY_OVERRIDES.settings, + }, + dataViewOrigin: 'metrics', +}; + +export const normalizedLoad1m: XYConfig = { + id: 'normalizedLoad1m', + title: i18n.translate('xpack.infra.assetDetails.metricsCharts.normalizedLoad1m', { + defaultMessage: 'Normalized Load', + }), + layers: [ + { + data: [hostLensFormulas.normalizedLoad1m], + type: 'visualization', + }, + { + data: [REFERENCE_LINE], + type: 'referenceLines', + }, + ], + dataViewOrigin: 'metrics', +}; + +export const loadBreakdown: XYConfig = { + id: 'loadBreakdown', + title: i18n.translate('xpack.infra.assetDetails.metricsCharts.load', { + defaultMessage: 'Load', + }), + layers: [ + { + data: [hostLensFormulas.load1m, hostLensFormulas.load5m, hostLensFormulas.load15m], + options: { + seriesType: 'area', + }, + type: 'visualization', + }, + ], + overrides: { + settings: XY_OVERRIDES.settings, + }, + dataViewOrigin: 'metrics', +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/disk.ts b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/disk.ts new file mode 100644 index 0000000000000..8d2daa435fbdc --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/disk.ts @@ -0,0 +1,150 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { hostLensFormulas } from '../../../../constants'; +import { XY_OVERRIDES } from '../../constants'; +import type { XYConfig } from './types'; + +const TOP_VALUES_SIZE = 5; + +export const diskSpaceUsageAvailable: XYConfig = { + id: 'diskSpaceUsageAvailable', + title: i18n.translate('xpack.infra.assetDetails.metricsCharts.diskSpace', { + defaultMessage: 'Disk Space', + }), + layers: [ + { + data: [ + { + ...hostLensFormulas.diskSpaceUsage, + label: i18n.translate('xpack.infra.assetDetails.metricsCharts.diskSpace.label.used', { + defaultMessage: 'Used', + }), + }, + { + ...hostLensFormulas.diskSpaceAvailability, + label: i18n.translate( + 'xpack.infra.assetDetails.metricsCharts.diskSpace.label.available', + { + defaultMessage: 'Available', + } + ), + }, + ], + options: { + seriesType: 'area', + }, + type: 'visualization', + }, + ], + overrides: { + axisLeft: XY_OVERRIDES.axisLeft, + settings: XY_OVERRIDES.settings, + }, + dataViewOrigin: 'metrics', +}; + +export const diskSpaceUsageByMountPoint: XYConfig = { + id: 'DiskSpaceUsageByMountPoint', + title: i18n.translate('xpack.infra.assetDetails.metricsCharts.diskSpaceByMountingPoint', { + defaultMessage: 'Disk Space by Mount Point', + }), + layers: [ + { + data: [ + { + ...hostLensFormulas.diskSpaceUsage, + label: i18n.translate('xpack.infra.assetDetails.metricsCharts.diskSpace.label.used', { + defaultMessage: 'Used', + }), + }, + ], + options: { + seriesType: 'area', + breakdown: { + type: 'top_values', + field: 'system.filesystem.mount_point', + params: { + size: TOP_VALUES_SIZE, + }, + }, + }, + type: 'visualization', + }, + ], + overrides: { + axisLeft: XY_OVERRIDES.axisLeft, + }, + dataViewOrigin: 'metrics', +}; + +export const diskThroughputReadWrite: XYConfig = { + id: 'diskThroughputReadWrite', + title: i18n.translate('xpack.infra.assetDetails.metricsCharts.diskIOPS', { + defaultMessage: 'Disk IOPS', + }), + layers: [ + { + data: [ + { + ...hostLensFormulas.diskIORead, + label: i18n.translate('xpack.infra.assetDetails.metricsCharts.metric.label.read', { + defaultMessage: 'Read', + }), + }, + { + ...hostLensFormulas.diskIOWrite, + label: i18n.translate('xpack.infra.assetDetails.metricsCharts.metric.label.write', { + defaultMessage: 'Write', + }), + }, + ], + options: { + seriesType: 'area', + }, + type: 'visualization', + }, + ], + overrides: { + settings: XY_OVERRIDES.settings, + }, + dataViewOrigin: 'metrics', +}; + +export const diskIOReadWrite: XYConfig = { + id: 'diskIOReadWrite', + title: i18n.translate('xpack.infra.assetDetails.metricsCharts.diskThroughput', { + defaultMessage: 'Disk Throughput', + }), + layers: [ + { + data: [ + { + ...hostLensFormulas.diskReadThroughput, + label: i18n.translate('xpack.infra.assetDetails.metricsCharts.metric.label.read', { + defaultMessage: 'Read', + }), + }, + { + ...hostLensFormulas.diskWriteThroughput, + label: i18n.translate('xpack.infra.assetDetails.metricsCharts.metric.label.write', { + defaultMessage: 'Write', + }), + }, + ], + options: { + seriesType: 'area', + }, + type: 'visualization', + }, + ], + overrides: { + settings: XY_OVERRIDES.settings, + }, + dataViewOrigin: 'metrics', +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/log.ts b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/log.ts new file mode 100644 index 0000000000000..811b0faa32a23 --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/log.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { hostLensFormulas } from '../../../../constants'; +import type { XYConfig } from './types'; + +export const logRate: XYConfig = { + id: 'logRate', + title: i18n.translate('xpack.infra.assetDetails.metricsCharts.logRate', { + defaultMessage: 'Log Rate', + }), + layers: [ + { + data: [hostLensFormulas.logRate], + type: 'visualization', + }, + ], + dataViewOrigin: 'logs', +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/memory.ts b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/memory.ts new file mode 100644 index 0000000000000..c933a6fc424fa --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/memory.ts @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { hostLensFormulas } from '../../../../constants'; +import { XY_OVERRIDES } from '../../constants'; +import type { XYConfig } from './types'; + +export const memoryUsage: XYConfig = { + id: 'memoryUsage', + title: i18n.translate('xpack.infra.assetDetails.metricsCharts.memoryUsage', { + defaultMessage: 'Memory Usage', + }), + layers: [ + { + data: [hostLensFormulas.memoryUsage], + type: 'visualization', + }, + ], + dataViewOrigin: 'metrics', + overrides: { + axisLeft: XY_OVERRIDES.axisLeft, + }, +}; + +export const memoryUsageBreakdown: XYConfig = { + id: 'memoryUsage', + title: i18n.translate('xpack.infra.assetDetails.metricsCharts.memoryUsage', { + defaultMessage: 'Memory Usage', + }), + layers: [ + { + data: [ + { + ...hostLensFormulas.memoryCache, + label: i18n.translate('xpack.infra.assetDetails.metricsCharts.metric.label.cache', { + defaultMessage: 'Cache', + }), + }, + { + ...hostLensFormulas.memoryUsed, + label: i18n.translate('xpack.infra.assetDetails.metricsCharts.metric.label.used', { + defaultMessage: 'Used', + }), + }, + { + ...hostLensFormulas.memoryFreeExcludingCache, + label: i18n.translate('xpack.infra.assetDetails.metricsCharts.metric.label.free', { + defaultMessage: 'Free', + }), + }, + ], + options: { + seriesType: 'area_stacked', + }, + type: 'visualization', + }, + ], + overrides: { + settings: XY_OVERRIDES.settings, + }, + dataViewOrigin: 'metrics', +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/network.ts b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/network.ts new file mode 100644 index 0000000000000..5baa9d6d2e489 --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/network.ts @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { hostLensFormulas } from '../../../../constants'; +import { XY_OVERRIDES } from '../../constants'; +import type { XYConfig } from './types'; + +export const rxTx: XYConfig = { + id: 'rxTx', + title: i18n.translate('xpack.infra.assetDetails.metricsCharts.network', { + defaultMessage: 'Network', + }), + layers: [ + { + data: [ + { + ...hostLensFormulas.rx, + label: i18n.translate('xpack.infra.assetDetails.metricsCharts.network.label.rx', { + defaultMessage: 'Inbound (RX)', + }), + }, + { + ...hostLensFormulas.tx, + label: i18n.translate('xpack.infra.assetDetails.metricsCharts.network.label.tx', { + defaultMessage: 'Outbound (TX)', + }), + }, + ], + options: { + seriesType: 'area', + }, + type: 'visualization', + }, + ], + overrides: { + settings: XY_OVERRIDES.settings, + }, + dataViewOrigin: 'metrics', +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/types.ts b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/types.ts new file mode 100644 index 0000000000000..0daff60793d52 --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/types.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. + */ +import type { TypedLensByValueInput } from '@kbn/lens-plugin/public'; +import type { DataViewOrigin } from '../../../../../../components/asset_details/types'; +import type { XYChartLayerParams } from '../../../../types'; + +export type XYConfig = Pick & { + dataViewOrigin: DataViewOrigin; + layers: XYChartLayerParams[]; +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/constants.ts b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/constants.ts index 9d37c3cc9596f..5efc100ada739 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/constants.ts +++ b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/constants.ts @@ -34,7 +34,7 @@ export const XY_OVERRIDES: XYOverrides = { settings: { showLegend: true, legendPosition: 'bottom', - legendSize: 35, + legendSize: 50, }, }; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_iowait.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_iowait.ts new file mode 100644 index 0000000000000..3e811a7ad517f --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_iowait.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; + +export const cpuUsageIowait: FormulaValueConfig = { + label: 'iowait', + value: 'average(system.cpu.iowait.pct) / max(system.cpu.cores)', + format: { + id: 'percent', + params: { + decimals: 1, + }, + }, +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_irq.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_irq.ts new file mode 100644 index 0000000000000..68cdbe9b65ea5 --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_irq.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; + +export const cpuUsageIrq: FormulaValueConfig = { + label: 'irq', + value: 'average(system.cpu.irq.pct) / max(system.cpu.cores)', + format: { + id: 'percent', + params: { + decimals: 1, + }, + }, +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_nice.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_nice.ts new file mode 100644 index 0000000000000..6ab8e01fec0c7 --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_nice.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; + +export const cpuUsageNice: FormulaValueConfig = { + label: 'nice', + value: 'average(system.cpu.nice.norm.pct) / max(system.cpu.cores)', + format: { + id: 'percent', + params: { + decimals: 1, + }, + }, +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_softirq.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_softirq.ts new file mode 100644 index 0000000000000..bb26685b9d8bc --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_softirq.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; + +export const cpuUsageSoftirq: FormulaValueConfig = { + label: 'softirq', + value: 'average(system.cpu.softirq.pct) / max(system.cpu.cores)', + format: { + id: 'percent', + params: { + decimals: 1, + }, + }, +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_steal.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_steal.ts new file mode 100644 index 0000000000000..ec0fa2652427c --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_steal.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; + +export const cpuUsageSteal: FormulaValueConfig = { + label: 'steal', + value: 'average(system.cpu.steal.pct) / max(system.cpu.cores)', + format: { + id: 'percent', + params: { + decimals: 1, + }, + }, +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_system.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_system.ts new file mode 100644 index 0000000000000..303ff89aa256e --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_system.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; + +export const cpuUsageSystem: FormulaValueConfig = { + label: 'system', + value: 'average(system.cpu.system.pct) / max(system.cpu.cores)', + format: { + id: 'percent', + params: { + decimals: 1, + }, + }, +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_user.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_user.ts new file mode 100644 index 0000000000000..d24c9b82a199a --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_user.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; + +export const cpuUsageUser: FormulaValueConfig = { + label: 'user', + value: 'average(system.cpu.user.pct) / max(system.cpu.cores)', + format: { + id: 'percent', + params: { + decimals: 1, + }, + }, +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/index.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/index.ts index 9cec01155ac19..1741741951e1a 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/index.ts +++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/index.ts @@ -6,6 +6,13 @@ */ export { cpuUsage } from './cpu_usage'; +export { cpuUsageIowait } from './cpu_usage_iowait'; +export { cpuUsageIrq } from './cpu_usage_irq'; +export { cpuUsageNice } from './cpu_usage_nice'; +export { cpuUsageSoftirq } from './cpu_usage_softirq'; +export { cpuUsageSteal } from './cpu_usage_steal'; +export { cpuUsageUser } from './cpu_usage_user'; +export { cpuUsageSystem } from './cpu_usage_system'; export { diskIORead } from './disk_read_iops'; export { diskIOWrite } from './disk_write_iops'; export { diskReadThroughput } from './disk_read_throughput'; @@ -16,7 +23,13 @@ export { diskSpaceUsage } from './disk_space_usage'; export { hostCount } from './host_count'; export { logRate } from './log_rate'; export { normalizedLoad1m } from './normalized_load_1m'; +export { load1m } from './load_1m'; +export { load5m } from './load_5m'; +export { load15m } from './load_15m'; export { memoryUsage } from './memory_usage'; export { memoryFree } from './memory_free'; +export { memoryUsed } from './memory_used'; +export { memoryFreeExcludingCache } from './memory_free_excluding_cache'; +export { memoryCache } from './memory_cache'; export { rx } from './rx'; export { tx } from './tx'; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/load_15m.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/load_15m.ts new file mode 100644 index 0000000000000..f34ee8aa31bdf --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/load_15m.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; + +export const load15m: FormulaValueConfig = { + label: 'Load (15m)', + value: 'average(system.load.15)', + format: { + id: 'number', + params: { + decimals: 1, + }, + }, +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/load_1m.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/load_1m.ts new file mode 100644 index 0000000000000..ac11637fdab56 --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/load_1m.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; + +export const load1m: FormulaValueConfig = { + label: 'Load (1m)', + value: 'average(system.load.1)', + format: { + id: 'number', + params: { + decimals: 1, + }, + }, +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/load_5m.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/load_5m.ts new file mode 100644 index 0000000000000..1fb8c15fc5888 --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/load_5m.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; + +export const load5m: FormulaValueConfig = { + label: 'Load (5m)', + value: 'average(system.load.5)', + format: { + id: 'number', + params: { + decimals: 1, + }, + }, +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_cache.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_cache.ts new file mode 100644 index 0000000000000..6688f51fb49c5 --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_cache.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; + +export const memoryCache: FormulaValueConfig = { + label: 'cache', + value: 'average(system.memory.used.bytes) - average(system.memory.actual.used.bytes)', + format: { + id: 'bytes', + params: { + decimals: 1, + }, + }, +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_free_excluding_cache.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_free_excluding_cache.ts new file mode 100644 index 0000000000000..96890b22a7472 --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_free_excluding_cache.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; + +export const memoryFreeExcludingCache: FormulaValueConfig = { + label: 'free', + value: 'average(system.memory.free)', + format: { + id: 'bytes', + params: { + decimals: 1, + }, + }, +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_used.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_used.ts new file mode 100644 index 0000000000000..8df1f24353d6a --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_used.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; + +export const memoryUsed: FormulaValueConfig = { + label: 'used', + value: 'average(system.memory.actual.used.bytes)', + format: { + id: 'bytes', + params: { + decimals: 1, + }, + }, +}; diff --git a/x-pack/plugins/infra/public/components/asset_details/asset_details.tsx b/x-pack/plugins/infra/public/components/asset_details/asset_details.tsx index 95c5b4836a196..8b3a4b2c706d8 100644 --- a/x-pack/plugins/infra/public/components/asset_details/asset_details.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/asset_details.tsx @@ -28,15 +28,12 @@ export const AssetDetails = ({ tabs, links, renderMode, - activeTabId, metricAlias, ...props }: AssetDetailsProps) => { return ( - 0 ? activeTabId ?? tabs[0].id : undefined} - > + diff --git a/x-pack/plugins/infra/public/components/asset_details/asset_details_embeddable.tsx b/x-pack/plugins/infra/public/components/asset_details/asset_details_embeddable.tsx index 3aff0ea4b2548..45bfb7d46a59f 100644 --- a/x-pack/plugins/infra/public/components/asset_details/asset_details_embeddable.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/asset_details_embeddable.tsx @@ -71,14 +71,12 @@ export class AssetDetailsEmbeddable extends Embeddable
    diff --git a/x-pack/plugins/infra/public/components/asset_details/context_providers.tsx b/x-pack/plugins/infra/public/components/asset_details/context_providers.tsx index 6af455dff2b02..a275849e18edf 100644 --- a/x-pack/plugins/infra/public/components/asset_details/context_providers.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/context_providers.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { AssetDetailsStateProvider } from './hooks/use_asset_details_state'; +import { AssetDetailsRenderPropsProvider } from './hooks/use_asset_details_render_props'; import { DateRangeProvider } from './hooks/use_date_range'; import { MetadataStateProvider } from './hooks/use_metadata_state'; import { AssetDetailsProps } from './types'; @@ -17,21 +17,20 @@ export const ContextProviders = ({ }: { props: Omit } & { children: React.ReactNode; }) => { - const { asset, dateRange, overrides, onTabsStateChange, assetType = 'host', renderMode } = props; + const { asset, dateRange, overrides, assetType = 'host', renderMode } = props; return ( - {children} - + ); diff --git a/x-pack/plugins/infra/public/components/asset_details/date_picker/date_picker.tsx b/x-pack/plugins/infra/public/components/asset_details/date_picker/date_picker.tsx index b4ec1128494d8..c7a90a3601c6c 100644 --- a/x-pack/plugins/infra/public/components/asset_details/date_picker/date_picker.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/date_picker/date_picker.tsx @@ -7,22 +7,17 @@ import { EuiSuperDatePicker, type OnTimeChangeProps } from '@elastic/eui'; import React, { useCallback } from 'react'; -import { useAssetDetailsStateContext } from '../hooks/use_asset_details_state'; import { useDateRangeProviderContext } from '../hooks/use_date_range'; export const DatePicker = () => { - const { onTabsStateChange } = useAssetDetailsStateContext(); const { dateRange, setDateRange } = useDateRangeProviderContext(); const onTimeChange = useCallback( ({ start, end, isInvalid }: OnTimeChangeProps) => { if (!isInvalid) { setDateRange({ from: start, to: end }); - if (onTabsStateChange) { - onTabsStateChange({ dateRange: { from: start, to: end } }); - } } }, - [onTabsStateChange, setDateRange] + [setDateRange] ); return ( diff --git a/x-pack/plugins/infra/public/components/asset_details/hooks/use_asset_details_state.ts b/x-pack/plugins/infra/public/components/asset_details/hooks/use_asset_details_render_props.ts similarity index 61% rename from x-pack/plugins/infra/public/components/asset_details/hooks/use_asset_details_state.ts rename to x-pack/plugins/infra/public/components/asset_details/hooks/use_asset_details_render_props.ts index 53bb74beef943..6ddba44e6da7e 100644 --- a/x-pack/plugins/infra/public/components/asset_details/hooks/use_asset_details_state.ts +++ b/x-pack/plugins/infra/public/components/asset_details/hooks/use_asset_details_render_props.ts @@ -9,16 +9,13 @@ import createContainer from 'constate'; import type { AssetDetailsProps } from '../types'; import { useMetadataStateProviderContext } from './use_metadata_state'; -export interface UseAssetDetailsStateProps { - state: Pick< - AssetDetailsProps, - 'asset' | 'assetType' | 'overrides' | 'onTabsStateChange' | 'renderMode' - >; +export interface UseAssetDetailsRenderProps { + props: Pick; } -export function useAssetDetailsState({ state }: UseAssetDetailsStateProps) { +export function useAssetDetailsRenderProps({ props }: UseAssetDetailsRenderProps) { const { metadata } = useMetadataStateProviderContext(); - const { asset, assetType, onTabsStateChange, overrides, renderMode } = state; + const { asset, assetType, overrides, renderMode } = props; // When the asset asset.name is known we can load the page faster // Otherwise we need to use metadata response. @@ -30,12 +27,12 @@ export function useAssetDetailsState({ state }: UseAssetDetailsStateProps) { name: asset.name || metadata?.name || 'asset-name', }, assetType, - onTabsStateChange, overrides, renderMode, loading, }; } -export const AssetDetailsState = createContainer(useAssetDetailsState); -export const [AssetDetailsStateProvider, useAssetDetailsStateContext] = AssetDetailsState; +export const AssetDetailsRenderProps = createContainer(useAssetDetailsRenderProps); +export const [AssetDetailsRenderPropsProvider, useAssetDetailsRenderPropsContext] = + AssetDetailsRenderProps; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_host_flyout_url_state.ts b/x-pack/plugins/infra/public/components/asset_details/hooks/use_asset_details_url_state.ts similarity index 53% rename from x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_host_flyout_url_state.ts rename to x-pack/plugins/infra/public/components/asset_details/hooks/use_asset_details_url_state.ts index 62d8fdb302d3e..370778555f7d0 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_host_flyout_url_state.ts +++ b/x-pack/plugins/infra/public/components/asset_details/hooks/use_asset_details_url_state.ts @@ -9,28 +9,27 @@ import * as rt from 'io-ts'; import { pipe } from 'fp-ts/lib/pipeable'; import { fold } from 'fp-ts/lib/Either'; import { constant, identity } from 'fp-ts/lib/function'; -import { FlyoutTabIds } from '../../../../components/asset_details/types'; -import { useUrlState } from '../../../../utils/use_url_state'; +import { FlyoutTabIds } from '../types'; +import { useUrlState } from '../../../utils/use_url_state'; -export const DEFAULT_STATE: HostFlyout = { - itemId: '', +export const DEFAULT_STATE: AssetDetailsState = { tabId: FlyoutTabIds.OVERVIEW, processSearch: undefined, metadataSearch: undefined, }; -const HOST_FLYOUT_URL_STATE_KEY = 'flyout'; +const ASSET_DETAILS_URL_STATE_KEY = 'asset_details'; -type SetHostFlyoutState = (newProp: Payload | null) => void; +type SetAssetDetailsState = (newProp: Payload | null) => void; -export const useHostFlyoutUrlState = (): [HostFlyoutUrl, SetHostFlyoutState] => { - const [urlState, setUrlState] = useUrlState({ +export const useAssetDetailsUrlState = (): [AssetDetailsUrl, SetAssetDetailsState] => { + const [urlState, setUrlState] = useUrlState({ defaultState: null, decodeUrlState, encodeUrlState, - urlStateKey: HOST_FLYOUT_URL_STATE_KEY, + urlStateKey: ASSET_DETAILS_URL_STATE_KEY, }); - const setHostFlyoutState = (newProps: Payload | null) => { + const setAssetDetailsState = (newProps: Payload | null) => { if (!newProps) { setUrlState(DEFAULT_STATE); } else { @@ -41,10 +40,10 @@ export const useHostFlyoutUrlState = (): [HostFlyoutUrl, SetHostFlyoutState] => } }; - return [urlState as HostFlyoutUrl, setHostFlyoutState]; + return [urlState as AssetDetailsUrl, setAssetDetailsState]; }; -const FlyoutTabIdRT = rt.union([ +const TabIdRT = rt.union([ rt.literal(FlyoutTabIds.OVERVIEW), rt.literal(FlyoutTabIds.METADATA), rt.literal(FlyoutTabIds.PROCESSES), @@ -53,10 +52,9 @@ const FlyoutTabIdRT = rt.union([ rt.literal(FlyoutTabIds.OSQUERY), ]); -const HostFlyoutStateRT = rt.intersection([ +const AssetDetailsStateRT = rt.intersection([ rt.type({ - itemId: rt.string, - tabId: FlyoutTabIdRT, + tabId: TabIdRT, }), rt.partial({ dateRange: rt.type({ @@ -69,14 +67,13 @@ const HostFlyoutStateRT = rt.intersection([ }), ]); -const HostFlyoutUrlRT = rt.union([HostFlyoutStateRT, rt.null]); +const AssetDetailsUrlRT = rt.union([AssetDetailsStateRT, rt.null]); -type HostFlyoutState = rt.TypeOf; -type HostFlyoutUrl = rt.TypeOf; -type Payload = Partial; -export type HostFlyout = rt.TypeOf; +export type AssetDetailsState = rt.TypeOf; +type AssetDetailsUrl = rt.TypeOf; +type Payload = Partial; -const encodeUrlState = HostFlyoutUrlRT.encode; +const encodeUrlState = AssetDetailsUrlRT.encode; const decodeUrlState = (value: unknown) => { - return pipe(HostFlyoutUrlRT.decode(value), fold(constant(undefined), identity)); + return pipe(AssetDetailsUrlRT.decode(value), fold(constant(undefined), identity)); }; diff --git a/x-pack/plugins/infra/public/components/asset_details/hooks/use_date_range.ts b/x-pack/plugins/infra/public/components/asset_details/hooks/use_date_range.ts index 8253b7e8b5685..7e98c56834529 100644 --- a/x-pack/plugins/infra/public/components/asset_details/hooks/use_date_range.ts +++ b/x-pack/plugins/infra/public/components/asset_details/hooks/use_date_range.ts @@ -7,22 +7,31 @@ import type { TimeRange } from '@kbn/es-query'; import createContainer from 'constate'; -import { useCallback, useMemo, useState } from 'react'; +import { useCallback, useMemo } from 'react'; import { parseDateRange } from '../../../utils/datemath'; import { toTimestampRange } from '../utils'; +import { useAssetDetailsUrlState } from './use_asset_details_url_state'; const DEFAULT_DATE_RANGE: TimeRange = { from: 'now-15m', to: 'now', }; -export interface UseAssetDetailsStateProps { +export interface UseDateRangeProviderProps { initialDateRange: TimeRange; } -export function useDateRangeProvider({ initialDateRange }: UseAssetDetailsStateProps) { - const [dateRange, setDateRange] = useState(initialDateRange); +export function useDateRangeProvider({ initialDateRange }: UseDateRangeProviderProps) { + const [urlState, setUrlState] = useAssetDetailsUrlState(); + const dateRange: TimeRange = urlState?.dateRange ?? initialDateRange; + + const setDateRange = useCallback( + (newDateRange: TimeRange) => { + setUrlState({ dateRange: newDateRange }); + }, + [setUrlState] + ); const parsedDateRange = useMemo(() => { const { from = DEFAULT_DATE_RANGE.from, to = DEFAULT_DATE_RANGE.to } = diff --git a/x-pack/plugins/infra/public/components/asset_details/hooks/use_page_header.tsx b/x-pack/plugins/infra/public/components/asset_details/hooks/use_page_header.tsx index da8d99b433821..07baa3c5f589b 100644 --- a/x-pack/plugins/infra/public/components/asset_details/hooks/use_page_header.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/hooks/use_page_header.tsx @@ -13,7 +13,7 @@ import { capitalize } from 'lodash'; import { APM_HOST_FILTER_FIELD } from '../constants'; import { LinkToAlertsRule, LinkToApmServices, LinkToNodeDetails } from '../links'; import { FlyoutTabIds, type LinkOptions, type Tab, type TabIds } from '../types'; -import { useAssetDetailsStateContext } from './use_asset_details_state'; +import { useAssetDetailsRenderPropsContext } from './use_asset_details_render_props'; import { useDateRangeProviderContext } from './use_date_range'; import { useTabSwitcherContext } from './use_tab_switcher'; @@ -28,7 +28,7 @@ export const usePageHeader = (tabs: Tab[], links?: LinkOptions[]) => { const useRightSideItems = (links?: LinkOptions[]) => { const { getDateRangeInTimestamp } = useDateRangeProviderContext(); - const { asset, assetType, overrides } = useAssetDetailsStateContext(); + const { asset, assetType, overrides } = useAssetDetailsRenderPropsContext(); const topCornerLinkComponents: Record = useMemo( () => ({ @@ -55,7 +55,7 @@ const useRightSideItems = (links?: LinkOptions[]) => { const useTabs = (tabs: Tab[]) => { const { showTab, activeTabId } = useTabSwitcherContext(); - const { asset } = useAssetDetailsStateContext(); + const { asset } = useAssetDetailsRenderPropsContext(); const { euiTheme } = useEuiTheme(); const onTabClick = useCallback( diff --git a/x-pack/plugins/infra/public/components/asset_details/hooks/use_tab_switcher.tsx b/x-pack/plugins/infra/public/components/asset_details/hooks/use_tab_switcher.tsx index 6bdcbca214d37..5de6383099e1d 100644 --- a/x-pack/plugins/infra/public/components/asset_details/hooks/use_tab_switcher.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/hooks/use_tab_switcher.tsx @@ -5,35 +5,31 @@ * 2.0. */ -import { useState } from 'react'; import createContainer from 'constate'; import { useLazyRef } from '../../../hooks/use_lazy_ref'; import type { TabIds } from '../types'; -import { useAssetDetailsStateContext } from './use_asset_details_state'; +import { AssetDetailsState, useAssetDetailsUrlState } from './use_asset_details_url_state'; interface TabSwitcherParams { - initialActiveTabId?: TabIds; + defaultActiveTabId?: TabIds; } -export function useTabSwitcher({ initialActiveTabId }: TabSwitcherParams) { - const { onTabsStateChange } = useAssetDetailsStateContext(); - const [activeTabId, setActiveTabId] = useState(initialActiveTabId); +export function useTabSwitcher({ defaultActiveTabId }: TabSwitcherParams) { + const [urlState, setUrlState] = useAssetDetailsUrlState(); + const activeTabId: TabIds | undefined = urlState?.tabId || defaultActiveTabId; // This set keeps track of which tabs content have been rendered the first time. // We need it in order to load a tab content only if it gets clicked, and then keep it in the DOM for performance improvement. - const renderedTabsSet = useLazyRef(() => new Set([initialActiveTabId])); + const renderedTabsSet = useLazyRef(() => new Set([activeTabId])); const showTab = (tabId: TabIds) => { - renderedTabsSet.current.add(tabId); // On a tab click, mark the tab content as allowed to be rendered - setActiveTabId(tabId); + // On a tab click, mark the tab content as allowed to be rendered + renderedTabsSet.current.add(tabId); - if (onTabsStateChange) { - onTabsStateChange({ activeTabId: tabId }); - } + setUrlState({ tabId: tabId as AssetDetailsState['tabId'] }); }; return { - initialActiveTabId, activeTabId, renderedTabsSet, showTab, diff --git a/x-pack/plugins/infra/public/components/asset_details/links/link_to_node_details.tsx b/x-pack/plugins/infra/public/components/asset_details/links/link_to_node_details.tsx index d834a6ca9e0d7..4b0e8a52d400c 100644 --- a/x-pack/plugins/infra/public/components/asset_details/links/link_to_node_details.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/links/link_to_node_details.tsx @@ -10,7 +10,6 @@ import { EuiButtonEmpty } from '@elastic/eui'; import { useLinkProps } from '@kbn/observability-shared-plugin/public'; import { getNodeDetailUrl } from '../../../pages/link_to'; import type { InventoryItemType } from '../../../../common/inventory_models/types'; -import type { Asset } from '../types'; export interface LinkToNodeDetailsProps { dateRangeTimestamp: { from: number; to: number }; diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/anomalies/anomalies.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/anomalies/anomalies.tsx index 001678d6e6c01..7d6cabc54c3ab 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/anomalies/anomalies.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/anomalies/anomalies.tsx @@ -7,12 +7,12 @@ import React from 'react'; import { AnomaliesTable } from '../../../../pages/metrics/inventory_view/components/ml/anomaly_detection/anomalies_table/anomalies_table'; -import { useAssetDetailsStateContext } from '../../hooks/use_asset_details_state'; +import { useAssetDetailsRenderPropsContext } from '../../hooks/use_asset_details_render_props'; import { useDateRangeProviderContext } from '../../hooks/use_date_range'; export const Anomalies = () => { const { dateRange } = useDateRangeProviderContext(); - const { asset, overrides } = useAssetDetailsStateContext(); + const { asset, overrides } = useAssetDetailsRenderPropsContext(); const { onClose = () => {} } = overrides?.anomalies ?? {}; return ( diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/logs/logs.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/logs/logs.tsx index 68f0ea074f292..a19861968335c 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/logs/logs.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/logs/logs.tsx @@ -16,33 +16,32 @@ import { DEFAULT_LOG_VIEW, LogViewReference } from '@kbn/logs-shared-plugin/comm import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana'; import { findInventoryFields } from '../../../../../common/inventory_models'; import { InfraLoadingPanel } from '../../../loading'; -import { useAssetDetailsStateContext } from '../../hooks/use_asset_details_state'; +import { useAssetDetailsRenderPropsContext } from '../../hooks/use_asset_details_render_props'; import { useDataViewsProviderContext } from '../../hooks/use_data_views'; import { useDateRangeProviderContext } from '../../hooks/use_date_range'; +import { useAssetDetailsUrlState } from '../../hooks/use_asset_details_url_state'; const TEXT_QUERY_THROTTLE_INTERVAL_MS = 500; export const Logs = () => { const { getDateRangeInTimestamp } = useDateRangeProviderContext(); - const { asset, assetType, overrides, onTabsStateChange } = useAssetDetailsStateContext(); + const [urlState, setUrlState] = useAssetDetailsUrlState(); + const { asset, assetType } = useAssetDetailsRenderPropsContext(); const { logs } = useDataViewsProviderContext(); - const { query: overrideQuery } = overrides?.logs ?? {}; const { loading: logViewLoading, reference: logViewReference } = logs ?? {}; const { services } = useKibanaContextForPlugin(); const { locators } = services; - const [textQuery, setTextQuery] = useState(overrideQuery ?? ''); - const [textQueryDebounced, setTextQueryDebounced] = useState(overrideQuery ?? ''); + const [textQuery, setTextQuery] = useState(urlState?.logsSearch ?? ''); + const [textQueryDebounced, setTextQueryDebounced] = useState(urlState?.logsSearch ?? ''); const currentTimestamp = getDateRangeInTimestamp().to; const startTimestamp = currentTimestamp - 60 * 60 * 1000; // 60 minutes useDebounce( () => { - if (onTabsStateChange) { - onTabsStateChange({ logs: { query: textQuery } }); - } + setUrlState({ logsSearch: textQuery }); setTextQueryDebounced(textQuery); }, TEXT_QUERY_THROTTLE_INTERVAL_MS, diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/add_metadata_filter_button.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/add_metadata_filter_button.tsx index a2d04b1c36184..49d912fab3f50 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/add_metadata_filter_button.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/add_metadata_filter_button.tsx @@ -10,8 +10,8 @@ import { i18n } from '@kbn/i18n'; import { EuiToolTip, EuiButtonIcon } from '@elastic/eui'; import { useMetricsDataViewContext } from '../../../../pages/metrics/hosts/hooks/use_data_view'; import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana'; -import { useUnifiedSearchContext } from '../../../../pages/metrics/hosts/hooks/use_unified_search'; import { buildMetadataFilter } from './build_metadata_filter'; +import { useUnifiedSearchContext } from '../../../../pages/metrics/hosts/hooks/use_unified_search'; interface AddMetadataFilterButtonProps { item: { diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/metadata.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/metadata.tsx index f3b27b9a8caca..7c7d4b92f468f 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/metadata.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/metadata.tsx @@ -12,7 +12,8 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { Table } from './table'; import { getAllFields } from './utils'; import { useMetadataStateProviderContext } from '../../hooks/use_metadata_state'; -import { useAssetDetailsStateContext } from '../../hooks/use_asset_details_state'; +import { useAssetDetailsRenderPropsContext } from '../../hooks/use_asset_details_render_props'; +import { useAssetDetailsUrlState } from '../../hooks/use_asset_details_url_state'; export interface MetadataSearchUrlState { metadataSearchUrlState: string; @@ -20,23 +21,22 @@ export interface MetadataSearchUrlState { } export const Metadata = () => { - const { overrides, onTabsStateChange } = useAssetDetailsStateContext(); + const [urlState, setUrlState] = useAssetDetailsUrlState(); + const { overrides } = useAssetDetailsRenderPropsContext(); const { metadata, loading: metadataLoading, error: fetchMetadataError, } = useMetadataStateProviderContext(); - const { query, showActionsColumn = false } = overrides?.metadata ?? {}; + const { showActionsColumn = false } = overrides?.metadata ?? {}; const fields = useMemo(() => getAllFields(metadata), [metadata]); const onSearchChange = useCallback( (newQuery: string) => { - if (onTabsStateChange) { - onTabsStateChange({ metadata: { query: newQuery } }); - } + setUrlState({ metadataSearch: newQuery }); }, - [onTabsStateChange] + [setUrlState] ); if (fetchMetadataError) { @@ -71,7 +71,7 @@ export const Metadata = () => { return ( >[0]; interface Props { @@ -29,10 +29,11 @@ interface Props { timeRange: TimeRange; metricsDataView?: DataView; logsDataView?: DataView; + isCompactView: boolean; } export const MetricsGrid = React.memo( - ({ nodeName, metricsDataView, logsDataView, timeRange }: Props) => { + ({ nodeName, metricsDataView, logsDataView, timeRange, isCompactView }: Props) => { const { setDateRange } = useDateRangeProviderContext(); const getDataView = useCallback( (dataViewOrigin: DataViewOrigin) => { @@ -78,26 +79,27 @@ export const MetricsGrid = React.memo( gutterSize="s" data-test-subj="infraAssetDetailsMetricsChartGrid" > - {assetDetailsDashboards.host.hostMetricCharts.map( - ({ dataViewOrigin, id, layers, title, overrides }, index) => ( - - - - ) - )} + {(isCompactView + ? assetDetailsDashboards.host.hostMetricCharts + : assetDetailsDashboards.host.hostMetricChartsFullPage + ).map(({ dataViewOrigin, id, layers, title, overrides }, index) => ( + + + + ))} diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/overview.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/overview.tsx index 3104fdf6b5f85..b6c805e3cc027 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/overview.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/overview.tsx @@ -14,14 +14,14 @@ import { MetadataSummaryList } from './metadata_summary/metadata_summary_list'; import { AlertsSummaryContent } from './alerts'; import { KPIGrid } from './kpis/kpi_grid'; import { MetricsGrid } from './metrics/metrics_grid'; -import { useAssetDetailsStateContext } from '../../hooks/use_asset_details_state'; +import { useAssetDetailsRenderPropsContext } from '../../hooks/use_asset_details_render_props'; import { useMetadataStateProviderContext } from '../../hooks/use_metadata_state'; import { useDataViewsProviderContext } from '../../hooks/use_data_views'; import { useDateRangeProviderContext } from '../../hooks/use_date_range'; export const Overview = () => { const { dateRange } = useDateRangeProviderContext(); - const { asset, assetType, renderMode } = useAssetDetailsStateContext(); + const { asset, assetType, renderMode } = useAssetDetailsRenderPropsContext(); const { metadata, loading: metadataLoading, @@ -80,6 +80,7 @@ export const Overview = () => { logsDataView={logs.dataView} metricsDataView={metrics.dataView} nodeName={asset.name} + isCompactView={renderMode?.mode === 'flyout'} /> diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/processes/processes.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/processes/processes.tsx index 0d96ff1d2264e..f0568332328dc 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/processes/processes.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/processes/processes.tsx @@ -29,8 +29,9 @@ import { ProcessListContextProvider, } from '../../../../pages/metrics/inventory_view/hooks/use_process_list'; import { getFieldByType } from '../../../../../common/inventory_models'; -import { useAssetDetailsStateContext } from '../../hooks/use_asset_details_state'; +import { useAssetDetailsRenderPropsContext } from '../../hooks/use_asset_details_render_props'; import { useDateRangeProviderContext } from '../../hooks/use_date_range'; +import { useAssetDetailsUrlState } from '../../hooks/use_asset_details_url_state'; const options = Object.entries(STATE_NAMES).map(([value, view]: [string, string]) => ({ value, @@ -39,11 +40,10 @@ const options = Object.entries(STATE_NAMES).map(([value, view]: [string, string] export const Processes = () => { const { getDateRangeInTimestamp } = useDateRangeProviderContext(); - const { asset, assetType, overrides, onTabsStateChange } = useAssetDetailsStateContext(); + const [urlState, setUrlState] = useAssetDetailsUrlState(); + const { asset, assetType } = useAssetDetailsRenderPropsContext(); - const { query: overrideQuery } = overrides?.processes ?? {}; - - const [searchText, setSearchText] = useState(overrideQuery ?? ''); + const [searchText, setSearchText] = useState(urlState?.processSearch ?? ''); const [searchBarState, setSearchBarState] = useState(() => searchText ? Query.parse(searchText) : Query.MATCH_ALL ); @@ -69,12 +69,10 @@ export const Processes = () => { const debouncedSearchOnChange = useMemo(() => { return debounce<(queryText: string) => void>((queryText) => { - if (onTabsStateChange) { - onTabsStateChange({ processes: { query: queryText } }); - } + setUrlState({ processSearch: queryText }); setSearchText(queryText); }, 500); - }, [onTabsStateChange]); + }, [setUrlState]); const searchBarOnChange = useCallback( ({ query, queryText }) => { @@ -86,11 +84,9 @@ export const Processes = () => { const clearSearchBar = useCallback(() => { setSearchBarState(Query.MATCH_ALL); - if (onTabsStateChange) { - onTabsStateChange({ processes: { query: '' } }); - } + setUrlState({ processSearch: '' }); setSearchText(''); - }, [onTabsStateChange]); + }, [setUrlState]); return ( @@ -139,6 +135,7 @@ export const Processes = () => { query={searchBarState} onChange={searchBarOnChange} box={{ + 'data-test-subj': 'infraAssetDetailsProcessesSearchBarInput', incremental: true, placeholder: i18n.translate('xpack.infra.metrics.nodeDetails.searchForProcesses', { defaultMessage: 'Search for processes…', diff --git a/x-pack/plugins/infra/public/components/asset_details/template/flyout.tsx b/x-pack/plugins/infra/public/components/asset_details/template/flyout.tsx index 9bc9a60777fad..f45aca765610d 100644 --- a/x-pack/plugins/infra/public/components/asset_details/template/flyout.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/template/flyout.tsx @@ -14,7 +14,7 @@ import { InfraLoadingPanel } from '../../loading'; import { ASSET_DETAILS_FLYOUT_COMPONENT_NAME } from '../constants'; import { Content } from '../content/content'; import { FlyoutHeader } from '../header/flyout_header'; -import { useAssetDetailsStateContext } from '../hooks/use_asset_details_state'; +import { useAssetDetailsRenderPropsContext } from '../hooks/use_asset_details_render_props'; import { usePageHeader } from '../hooks/use_page_header'; import { useTabSwitcherContext } from '../hooks/use_tab_switcher'; import type { ContentTemplateProps } from '../types'; @@ -23,9 +23,9 @@ export const Flyout = ({ header: { tabs = [], links = [] }, closeFlyout, }: ContentTemplateProps & { closeFlyout: () => void }) => { - const { asset, assetType, loading } = useAssetDetailsStateContext(); + const { asset, assetType, loading } = useAssetDetailsRenderPropsContext(); const { rightSideItems, tabEntries } = usePageHeader(tabs, links); - const { initialActiveTabId } = useTabSwitcherContext(); + const { activeTabId } = useTabSwitcherContext(); const { services: { telemetry }, } = useKibanaContextForPlugin(); @@ -34,7 +34,7 @@ export const Flyout = ({ telemetry.reportAssetDetailsFlyoutViewed({ componentName: ASSET_DETAILS_FLYOUT_COMPONENT_NAME, assetType, - tabId: initialActiveTabId, + tabId: activeTabId, }); }); diff --git a/x-pack/plugins/infra/public/components/asset_details/template/page.tsx b/x-pack/plugins/infra/public/components/asset_details/template/page.tsx index a95638bfca55a..bbf23196bf7ee 100644 --- a/x-pack/plugins/infra/public/components/asset_details/template/page.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/template/page.tsx @@ -12,12 +12,12 @@ import React from 'react'; import { useKibanaHeader } from '../../../hooks/use_kibana_header'; import { InfraLoadingPanel } from '../../loading'; import { Content } from '../content/content'; -import { useAssetDetailsStateContext } from '../hooks/use_asset_details_state'; +import { useAssetDetailsRenderPropsContext } from '../hooks/use_asset_details_render_props'; import { usePageHeader } from '../hooks/use_page_header'; import type { ContentTemplateProps } from '../types'; export const Page = ({ header: { tabs = [], links = [] } }: ContentTemplateProps) => { - const { asset, loading } = useAssetDetailsStateContext(); + const { asset, loading } = useAssetDetailsRenderPropsContext(); const { rightSideItems, tabEntries } = usePageHeader(tabs, links); const { headerHeight } = useKibanaHeader(); diff --git a/x-pack/plugins/infra/public/components/asset_details/types.ts b/x-pack/plugins/infra/public/components/asset_details/types.ts index 839154a4b9d33..b17c2c988eb27 100644 --- a/x-pack/plugins/infra/public/components/asset_details/types.ts +++ b/x-pack/plugins/infra/public/components/asset_details/types.ts @@ -28,21 +28,14 @@ export type TabIds = `${FlyoutTabIds}`; export interface OverridableTabState { metadata?: { - query?: string; showActionsColumn?: boolean; }; - processes?: { - query?: string; - }; anomalies?: { onClose?: () => void; }; alertRule?: { onCreateRuleClick?: () => void; }; - logs?: { - query?: string; - }; } export interface TabState extends OverridableTabState { @@ -72,10 +65,8 @@ export interface AssetDetailsProps { assetType: InventoryItemType; dateRange: TimeRange; tabs: Tab[]; - activeTabId?: TabIds; overrides?: OverridableTabState; renderMode: RenderMode; - onTabsStateChange?: TabsStateChangeFn; links?: LinkOptions[]; // This is temporary. Once we start using the asset details in other plugins, // It will have to retrieve the metricAlias internally rather than receive it via props @@ -87,3 +78,5 @@ export type TabsStateChangeFn = (state: TabState) => void; export interface ContentTemplateProps { header: Pick; } + +export type DataViewOrigin = 'logs' | 'metrics'; diff --git a/x-pack/plugins/infra/public/components/lens/lens_wrapper.tsx b/x-pack/plugins/infra/public/components/lens/lens_wrapper.tsx index 043173113c5a5..8513881c22ada 100644 --- a/x-pack/plugins/infra/public/components/lens/lens_wrapper.tsx +++ b/x-pack/plugins/infra/public/components/lens/lens_wrapper.tsx @@ -107,9 +107,6 @@ export const LensWrapper = ({ border-radius: ${euiTheme.size.s}; overflow: hidden; height: 100%; - .echLegend .echLegendList { - display: flex; - } .echMetric { border-radius: ${euiTheme.border.radius.medium}; pointer-events: none; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/host_details_flyout/flyout_wrapper.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/host_details_flyout/flyout_wrapper.tsx index ee4115a2ed981..065b6739c41fc 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/host_details_flyout/flyout_wrapper.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/host_details_flyout/flyout_wrapper.tsx @@ -10,9 +10,9 @@ import React from 'react'; import { useSourceContext } from '../../../../../containers/metrics_source'; import { useUnifiedSearchContext } from '../../hooks/use_unified_search'; import type { HostNodeRow } from '../../hooks/use_hosts_table'; -import { HostFlyout, useHostFlyoutUrlState } from '../../hooks/use_host_flyout_url_state'; import { AssetDetails } from '../../../../../components/asset_details/asset_details'; import { orderedFlyoutTabs } from './tabs'; +import { useAssetDetailsUrlState } from '../../../../../components/asset_details/hooks/use_asset_details_url_state'; export interface Props { node: HostNodeRow; @@ -22,35 +22,18 @@ export interface Props { export const FlyoutWrapper = ({ node: { name }, closeFlyout }: Props) => { const { source } = useSourceContext(); const { parsedDateRange } = useUnifiedSearchContext(); - const [hostFlyoutState, setHostFlyoutState] = useHostFlyoutUrlState(); + const [urlState] = useAssetDetailsUrlState(); return source ? ( - setHostFlyoutState({ - dateRange: state.dateRange, - metadataSearch: state.metadata?.query, - processSearch: state.processes?.query, - logsSearch: state.logs?.query, - tabId: state.activeTabId as HostFlyout['tabId'], - }) - } tabs={orderedFlyoutTabs} links={['apmServices', 'nodeDetails']} renderMode={{ diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx index 22677d692fdb9..19ba258e91b11 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx @@ -24,15 +24,14 @@ import type { InfraAssetMetricsItem, InfraAssetMetricType, } from '../../../../../common/http_api'; -import { useHostFlyoutUrlState } from './use_host_flyout_url_state'; import { Sorting, useHostsTableUrlState } from './use_hosts_table_url_state'; import { useHostsViewContext } from './use_hosts_view'; -import { useUnifiedSearchContext } from './use_unified_search'; import { useMetricsDataViewContext } from './use_data_view'; import { ColumnHeader } from '../components/table/column_header'; import { TABLE_COLUMN_LABEL } from '../translations'; import { METRICS_TOOLTIP } from '../../../../common/visualizations'; import { buildCombinedHostsFilter } from '../../../../utils/filters/build'; +import { useUnifiedSearchContext } from './use_unified_search'; /** * Columns and items types @@ -129,7 +128,7 @@ export const useHostsTable = () => { const [selectedItems, setSelectedItems] = useState([]); const { hostNodes } = useHostsViewContext(); const { parsedDateRange } = useUnifiedSearchContext(); - const [{ pagination, sorting }, setProperties] = useHostsTableUrlState(); + const [{ detailsItemId, pagination, sorting }, setProperties] = useHostsTableUrlState(); const { services: { telemetry, @@ -140,11 +139,10 @@ export const useHostsTable = () => { } = useKibanaContextForPlugin(); const { dataView } = useMetricsDataViewContext(); - const [hostFlyoutState, setHostFlyoutState] = useHostFlyoutUrlState(); const popoverContainerRef = useRef(null); const tableRef = useRef(null); - const closeFlyout = useCallback(() => setHostFlyoutState(null), [setHostFlyoutState]); + const closeFlyout = useCallback(() => setProperties({ detailsItemId: null }), [setProperties]); const onSelectionChange = (newSelectedItems: HostNodeRow[]) => { setSelectedItems(newSelectedItems); @@ -195,8 +193,8 @@ export const useHostsTable = () => { const items = useMemo(() => buildItemsList(hostNodes), [hostNodes]); const clickedItem = useMemo( - () => items.find(({ id }) => id === hostFlyoutState?.itemId), - [hostFlyoutState?.itemId, items] + () => items.find(({ id }) => id === detailsItemId), + [detailsItemId, items] ); const currentPage = useMemo(() => { @@ -218,19 +216,13 @@ export const useHostsTable = () => { { name: TABLE_COLUMN_LABEL.toggleDialogAction, description: TABLE_COLUMN_LABEL.toggleDialogAction, - icon: ({ id }) => - hostFlyoutState?.itemId && id === hostFlyoutState?.itemId ? 'minimize' : 'expand', + icon: ({ id }) => (id === detailsItemId ? 'minimize' : 'expand'), type: 'icon', 'data-test-subj': 'hostsView-flyout-button', onClick: ({ id }) => { - setHostFlyoutState({ - itemId: id, + setProperties({ + detailsItemId: id === detailsItemId ? null : id, }); - if (id === hostFlyoutState?.itemId) { - setHostFlyoutState(null); - } else { - setHostFlyoutState({ itemId: id }); - } }, }, ], @@ -351,7 +343,7 @@ export const useHostsTable = () => { width: '120px', }, ], - [hostFlyoutState?.itemId, parsedDateRange, reportHostEntryClick, setHostFlyoutState] + [detailsItemId, parsedDateRange, reportHostEntryClick, setProperties] ); const selection: EuiTableSelectionType = { @@ -365,7 +357,7 @@ export const useHostsTable = () => { currentPage, closeFlyout, items, - isFlyoutOpen: !!hostFlyoutState?.itemId, + isFlyoutOpen: detailsItemId !== null, onTableChange, pagination, sorting, diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table_url_state.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table_url_state.ts index 760ada008c8c0..c1dcafefaccc3 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table_url_state.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table_url_state.ts @@ -16,6 +16,7 @@ import { useUrlState } from '../../../../utils/use_url_state'; import { DEFAULT_PAGE_SIZE, LOCAL_STORAGE_PAGE_SIZE_KEY } from '../constants'; export const GET_DEFAULT_TABLE_PROPERTIES: TableProperties = { + detailsItemId: null, sorting: { direction: 'asc', field: 'name', @@ -29,7 +30,7 @@ export const GET_DEFAULT_TABLE_PROPERTIES: TableProperties = { const HOST_TABLE_PROPERTIES_URL_STATE_KEY = 'tableProperties'; const reducer = (prevState: TableProperties, params: Payload) => { - const payload = Object.fromEntries(Object.entries(params).filter(([_, v]) => !!v)); + const payload = Object.fromEntries(Object.entries(params).filter(([_, v]) => v !== undefined)); return { ...prevState, @@ -77,6 +78,7 @@ const SortingRT = rt.intersection([ ]); const TableStateRT = rt.type({ + detailsItemId: rt.union([rt.string, rt.null]), pagination: PaginationRT, sorting: SortingRT, }); diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/asset_detail_page.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/asset_detail_page.tsx index 31559352a67de..10af65db3fdd9 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/asset_detail_page.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/asset_detail_page.tsx @@ -5,19 +5,19 @@ * 2.0. */ -import type { TimeRange } from '@kbn/es-query'; -import React, { useCallback, useMemo } from 'react'; +import React, { useMemo } from 'react'; import { useLocation, useRouteMatch } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; +import type { TimeRange } from '@kbn/es-query'; import { NoRemoteCluster } from '../../../components/empty_states'; import { SourceErrorPage } from '../../../components/source_error_page'; import { SourceLoadingPage } from '../../../components/source_loading_page'; import { useSourceContext } from '../../../containers/metrics_source'; -import { FlyoutTabIds, type Tab, type TabState } from '../../../components/asset_details/types'; +import { FlyoutTabIds, type Tab } from '../../../components/asset_details/types'; import type { InventoryItemType } from '../../../../common/inventory_models/types'; import { AssetDetails } from '../../../components/asset_details/asset_details'; -import { useMetricsTimeContext } from './hooks/use_metrics_time'; import { MetricsPageTemplate } from '../page_template'; +import { useMetricsTimeContext } from './hooks/use_metrics_time'; const orderedFlyoutTabs: Tab[] = [ { @@ -70,7 +70,7 @@ export const AssetDetailPage = () => { return queryParams.get('assetName') ?? undefined; }, [search]); - const { timeRange, setTimeRange } = useMetricsTimeContext(); + const { timeRange } = useMetricsTimeContext(); const dateRange: TimeRange = useMemo( () => ({ @@ -83,23 +83,6 @@ export const AssetDetailPage = () => { [timeRange.from, timeRange.to] ); - // Retrocompatibility - const handleTabStateChange = useCallback( - ({ dateRange: newDateRange }: TabState) => { - if (newDateRange) { - setTimeRange( - { - from: newDateRange.from, - to: newDateRange.to, - interval: timeRange.interval, - }, - false - ); - } - }, - [setTimeRange, timeRange.interval] - ); - const { metricIndicesExist, remoteClustersExist } = source?.status ?? {}; if (isLoading || !source) return ; @@ -131,8 +114,6 @@ export const AssetDetailPage = () => { }} assetType={nodeType} dateRange={dateRange} - onTabsStateChange={handleTabStateChange} - activeTabId={FlyoutTabIds.OVERVIEW} tabs={orderedFlyoutTabs} links={['apmServices']} renderMode={{ diff --git a/x-pack/plugins/monitoring/server/alerts/base_rule.ts b/x-pack/plugins/monitoring/server/alerts/base_rule.ts index 17491ffc760cb..7c28080129351 100644 --- a/x-pack/plugins/monitoring/server/alerts/base_rule.ts +++ b/x-pack/plugins/monitoring/server/alerts/base_rule.ts @@ -191,14 +191,16 @@ export class BaseRule { return accum; } const alertInstance: RawAlertInstance = states.alertInstances[instanceId]; - const filteredAlertInstance = this.filterAlertInstance(alertInstance, filters); + const { state, ...filteredAlertInstance } = this.filterAlertInstance( + alertInstance, + filters + ); if (filteredAlertInstance) { - accum[instanceId] = filteredAlertInstance as RawAlertInstance; - if (filteredAlertInstance.state) { - accum[instanceId].state = { - alertStates: (filteredAlertInstance.state as AlertInstanceState).alertStates, - }; - } + accum[instanceId] = { + ...filteredAlertInstance, + // Only keep "alertStates" within the state + ...(state ? { state: { alertStates: state.alertStates } } : {}), + } as RawAlertInstance; } return accum; }, diff --git a/x-pack/plugins/observability/public/application/index.tsx b/x-pack/plugins/observability/public/application/index.tsx index f4be5a6247868..46dfd7a50d3ec 100644 --- a/x-pack/plugins/observability/public/application/index.tsx +++ b/x-pack/plugins/observability/public/application/index.tsx @@ -116,6 +116,7 @@ export const renderApp = ({ diff --git a/x-pack/plugins/observability/public/hooks/slo/use_fetch_active_alerts.ts b/x-pack/plugins/observability/public/hooks/slo/use_fetch_active_alerts.ts index 601fc8f024a89..7f4e6fdce5b2b 100644 --- a/x-pack/plugins/observability/public/hooks/slo/use_fetch_active_alerts.ts +++ b/x-pack/plugins/observability/public/hooks/slo/use_fetch_active_alerts.ts @@ -135,6 +135,7 @@ export function useFetchActiveAlerts({ sloIdsAndInstanceIds = [] }: Params): Use } }, refetchOnWindowFocus: false, + enabled: Boolean(sloIdsAndInstanceIds.length), }); return { diff --git a/x-pack/plugins/observability/public/hooks/slo/use_fetch_global_diagnosis.ts b/x-pack/plugins/observability/public/hooks/slo/use_fetch_global_diagnosis.ts index 94ec7c625a1b1..45ef877bf2472 100644 --- a/x-pack/plugins/observability/public/hooks/slo/use_fetch_global_diagnosis.ts +++ b/x-pack/plugins/observability/public/hooks/slo/use_fetch_global_diagnosis.ts @@ -5,32 +5,17 @@ * 2.0. */ -import { - QueryObserverResult, - RefetchOptions, - RefetchQueryFilters, - useQuery, -} from '@tanstack/react-query'; +import type { SecurityHasPrivilegesResponse } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { i18n } from '@kbn/i18n'; import type { PublicLicenseJSON } from '@kbn/licensing-plugin/public'; -import type { - SecurityGetUserPrivilegesResponse, - TransformGetTransformStatsResponse, -} from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { useQuery } from '@tanstack/react-query'; import { useKibana } from '../../utils/kibana_react'; import { convertErrorForUseInToast } from './helpers/convert_error_for_use_in_toast'; import { sloKeys } from './query_key_factory'; interface SloGlobalDiagnosisResponse { licenseAndFeatures: PublicLicenseJSON; - userPrivileges: SecurityGetUserPrivilegesResponse; - sloResources: { - [x: string]: 'OK' | 'NOT_OK'; - }; - sloSummaryResources: { - [x: string]: 'OK' | 'NOT_OK'; - }; - sloSummaryTransformsStats: TransformGetTransformStatsResponse; + userPrivileges: { write: SecurityHasPrivilegesResponse; read: SecurityHasPrivilegesResponse }; } export interface UseFetchSloGlobalDiagnoseResponse { @@ -39,10 +24,7 @@ export interface UseFetchSloGlobalDiagnoseResponse { isRefetching: boolean; isSuccess: boolean; isError: boolean; - globalSloDiagnosis: SloGlobalDiagnosisResponse | undefined; - refetch: ( - options?: (RefetchOptions & RefetchQueryFilters) | undefined - ) => Promise>; + data: SloGlobalDiagnosisResponse | undefined; } export function useFetchSloGlobalDiagnosis(): UseFetchSloGlobalDiagnoseResponse { @@ -51,44 +33,41 @@ export function useFetchSloGlobalDiagnosis(): UseFetchSloGlobalDiagnoseResponse notifications: { toasts }, } = useKibana().services; - const { isInitialLoading, isLoading, isError, isSuccess, isRefetching, data, refetch } = useQuery( - { - queryKey: sloKeys.globalDiagnosis(), - queryFn: async ({ signal }) => { - try { - const response = await http.get( - '/internal/observability/slos/_diagnosis', - { - query: {}, - signal, - } - ); + const { isInitialLoading, isLoading, isError, isSuccess, isRefetching, data } = useQuery({ + queryKey: sloKeys.globalDiagnosis(), + queryFn: async ({ signal }) => { + try { + const response = await http.get( + '/internal/observability/slos/_diagnosis', + { + query: {}, + signal, + } + ); - return response; - } catch (error) { - throw convertErrorForUseInToast(error); - } - }, - keepPreviousData: true, - refetchOnWindowFocus: false, - retry: false, - onError: (error: Error) => { - toasts.addError(error, { - title: i18n.translate('xpack.observability.slo.globalDiagnosis.errorNotification', { - defaultMessage: 'You do not have the right permissions to use this feature.', - }), - }); - }, - } - ); + return response; + } catch (error) { + throw convertErrorForUseInToast(error); + } + }, + keepPreviousData: true, + refetchOnWindowFocus: false, + retry: false, + onError: (error: Error) => { + toasts.addError(error, { + title: i18n.translate('xpack.observability.slo.globalDiagnosis.errorNotification', { + defaultMessage: 'You do not have the right permissions to use this feature.', + }), + }); + }, + }); return { - globalSloDiagnosis: data, + data, isLoading, isInitialLoading, isRefetching, isSuccess, isError, - refetch, }; } diff --git a/x-pack/plugins/observability/public/hooks/slo/use_fetch_index_pattern_fields.ts b/x-pack/plugins/observability/public/hooks/slo/use_fetch_index_pattern_fields.ts index 2f51e4d7faf26..3363d501fae22 100644 --- a/x-pack/plugins/observability/public/hooks/slo/use_fetch_index_pattern_fields.ts +++ b/x-pack/plugins/observability/public/hooks/slo/use_fetch_index_pattern_fields.ts @@ -34,7 +34,7 @@ export function useFetchIndexPatternFields( return []; } try { - return await dataViews.getFieldsForWildcard({ pattern: indexPattern }); + return await dataViews.getFieldsForWildcard({ pattern: indexPattern, allowNoIndex: true }); } catch (error) { throw new Error(`Something went wrong. Error: ${error}`); } diff --git a/x-pack/plugins/observability/public/hooks/slo/use_fetch_rules_for_slo.ts b/x-pack/plugins/observability/public/hooks/slo/use_fetch_rules_for_slo.ts index a31e6f8f35f47..21d34fc55289b 100644 --- a/x-pack/plugins/observability/public/hooks/slo/use_fetch_rules_for_slo.ts +++ b/x-pack/plugins/observability/public/hooks/slo/use_fetch_rules_for_slo.ts @@ -54,8 +54,7 @@ export function useFetchRulesForSlo({ sloIds = [] }: Params): UseFetchRulesForSl queryFn: async () => { try { const body = JSON.stringify({ - filter: - sloIds?.map((sloId) => `alert.attributes.params.sloId:${sloId}`).join(' or ') ?? '', + filter: sloIds.map((sloId) => `alert.attributes.params.sloId:${sloId}`).join(' or '), fields: ['params.sloId', 'name'], per_page: 1000, }); @@ -64,7 +63,7 @@ export function useFetchRulesForSlo({ sloIds = [] }: Params): UseFetchRulesForSl body, }); - const init = sloIds?.reduce((acc, sloId) => ({ ...acc, [sloId]: [] }), {}); + const init = sloIds.reduce((acc, sloId) => ({ ...acc, [sloId]: [] }), {}); return response.data.reduce( (acc, rule) => ({ @@ -77,7 +76,7 @@ export function useFetchRulesForSlo({ sloIds = [] }: Params): UseFetchRulesForSl // ignore error for retrieving slos } }, - enabled: Boolean(sloIds?.length), + enabled: Boolean(sloIds.length), refetchOnWindowFocus: false, keepPreviousData: true, } diff --git a/x-pack/plugins/observability/public/pages/slos_welcome/slos_welcome.test.tsx b/x-pack/plugins/observability/public/pages/slos_welcome/slos_welcome.test.tsx index 155e49c3976fc..fbeae4cb872b2 100644 --- a/x-pack/plugins/observability/public/pages/slos_welcome/slos_welcome.test.tsx +++ b/x-pack/plugins/observability/public/pages/slos_welcome/slos_welcome.test.tsx @@ -59,7 +59,9 @@ describe('SLOs Welcome Page', () => { useFetchSloListMock.mockReturnValue({ isLoading: false, sloList: emptySloList }); useLicenseMock.mockReturnValue({ hasAtLeast: () => false }); useGlobalDiagnosisMock.mockReturnValue({ - isError: false, + data: { + userPrivileges: { write: { has_all_requested: true }, read: { has_all_requested: true } }, + }, }); render(); @@ -104,7 +106,12 @@ describe('SLOs Welcome Page', () => { hasReadCapabilities: true, }); useGlobalDiagnosisMock.mockReturnValue({ - isError: true, + data: { + userPrivileges: { + write: { has_all_requested: false }, + read: { has_all_requested: true }, + }, + }, }); render(); @@ -116,7 +123,12 @@ describe('SLOs Welcome Page', () => { it('should display the welcome message with a Create new SLO button which should navigate to the SLO Creation page', async () => { useGlobalDiagnosisMock.mockReturnValue({ - isError: false, + data: { + userPrivileges: { + write: { has_all_requested: true }, + read: { has_all_requested: true }, + }, + }, }); render(); @@ -136,7 +148,12 @@ describe('SLOs Welcome Page', () => { beforeEach(() => { useFetchSloListMock.mockReturnValue({ isLoading: false, sloList }); useGlobalDiagnosisMock.mockReturnValue({ - isError: false, + data: { + userPrivileges: { + write: { has_all_requested: true }, + read: { has_all_requested: true }, + }, + }, }); }); diff --git a/x-pack/plugins/observability/public/pages/slos_welcome/slos_welcome.tsx b/x-pack/plugins/observability/public/pages/slos_welcome/slos_welcome.tsx index 2f0ef6ad1389e..5c4317cd4e138 100644 --- a/x-pack/plugins/observability/public/pages/slos_welcome/slos_welcome.tsx +++ b/x-pack/plugins/observability/public/pages/slos_welcome/slos_welcome.tsx @@ -34,7 +34,7 @@ export function SlosWelcomePage() { http: { basePath }, } = useKibana().services; const { hasWriteCapabilities } = useCapabilities(); - const { isError: hasErrorInGlobalDiagnosis } = useFetchSloGlobalDiagnosis(); + const { data: globalDiagnosis } = useFetchSloGlobalDiagnosis(); const { ObservabilityPageTemplate } = usePluginContext(); const { hasAtLeast } = useLicense(); @@ -43,12 +43,15 @@ export function SlosWelcomePage() { const { isLoading, sloList } = useFetchSloList(); const { total } = sloList || { total: 0 }; + const hasRequiredWritePrivileges = !!globalDiagnosis?.userPrivileges.write.has_all_requested; + const hasRequiredReadPrivileges = !!globalDiagnosis?.userPrivileges.read.has_all_requested; + const handleClickCreateSlo = () => { navigateToUrl(basePath.prepend(paths.observability.sloCreate)); }; const hasSlosAndHasPermissions = - total > 0 && hasAtLeast('platinum') === true && !hasErrorInGlobalDiagnosis; + total > 0 && hasAtLeast('platinum') === true && hasRequiredReadPrivileges; useEffect(() => { if (hasSlosAndHasPermissions) { @@ -115,7 +118,7 @@ export function SlosWelcomePage() { fill color="primary" onClick={handleClickCreateSlo} - disabled={!hasWriteCapabilities || hasErrorInGlobalDiagnosis} + disabled={!hasWriteCapabilities || !hasRequiredWritePrivileges} > {i18n.translate('xpack.observability.slo.sloList.welcomePrompt.buttonLabel', { defaultMessage: 'Create SLO', diff --git a/x-pack/plugins/observability/public/rules/register_observability_rule_types.ts b/x-pack/plugins/observability/public/rules/register_observability_rule_types.ts index 18d2ae5aa998b..c06bd84e02c77 100644 --- a/x-pack/plugins/observability/public/rules/register_observability_rule_types.ts +++ b/x-pack/plugins/observability/public/rules/register_observability_rule_types.ts @@ -9,7 +9,7 @@ import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; import { ALERT_REASON } from '@kbn/rule-data-utils'; -import { SLO_ID_FIELD } from '../../common/field_names/slo'; +import { SLO_ID_FIELD, SLO_INSTANCE_ID_FIELD } from '../../common/field_names/slo'; import { ConfigSchema } from '../plugin'; import { ObservabilityRuleTypeRegistry } from './create_observability_rule_type_registry'; import { @@ -86,7 +86,9 @@ export const registerObservabilityRuleTypes = ( format: ({ fields }) => { return { reason: fields[ALERT_REASON] ?? '-', - link: `/app/observability/slos/${fields[SLO_ID_FIELD]}`, + link: `/app/observability/slos/${fields[SLO_ID_FIELD]}?instanceId=${ + fields[SLO_INSTANCE_ID_FIELD] ?? '*' + }`, }; }, iconClass: 'bell', diff --git a/x-pack/plugins/observability/server/assets/component_templates/slo_summary_settings_template.ts b/x-pack/plugins/observability/server/assets/component_templates/slo_summary_settings_template.ts index 286434003f540..336b7d9e0c35c 100644 --- a/x-pack/plugins/observability/server/assets/component_templates/slo_summary_settings_template.ts +++ b/x-pack/plugins/observability/server/assets/component_templates/slo_summary_settings_template.ts @@ -11,7 +11,7 @@ export const getSLOSummarySettingsTemplate = (name: string) => ({ name, template: { settings: { - auto_expand_replicas: '0-all', + auto_expand_replicas: '0-1', hidden: true, }, }, diff --git a/x-pack/plugins/observability/server/routes/slo/route.ts b/x-pack/plugins/observability/server/routes/slo/route.ts index e991f2b6a9d4f..f77f173675c68 100644 --- a/x-pack/plugins/observability/server/routes/slo/route.ts +++ b/x-pack/plugins/observability/server/routes/slo/route.ts @@ -15,7 +15,6 @@ import { findSLOParamsSchema, getPreviewDataParamsSchema, getSLOBurnRatesParamsSchema, - getSLODiagnosisParamsSchema, getSLOInstancesParamsSchema, getSLOParamsSchema, manageSLOParamsSchema, @@ -35,7 +34,7 @@ import { import { FetchHistoricalSummary } from '../../services/slo/fetch_historical_summary'; import { FindSLODefinitions } from '../../services/slo/find_slo_definitions'; import { getBurnRates } from '../../services/slo/get_burn_rates'; -import { getGlobalDiagnosis, getSloDiagnosis } from '../../services/slo/get_diagnosis'; +import { getGlobalDiagnosis } from '../../services/slo/get_diagnosis'; import { GetPreviewData } from '../../services/slo/get_preview_data'; import { GetSLOInstances } from '../../services/slo/get_slo_instances'; import { DefaultHistoricalSummaryClient } from '../../services/slo/historical_summary_client'; @@ -310,21 +309,6 @@ const getDiagnosisRoute = createObservabilityServerRoute({ }, }); -const getSloDiagnosisRoute = createObservabilityServerRoute({ - endpoint: 'GET /internal/observability/slos/{id}/_diagnosis', - options: { - tags: [], - }, - params: getSLODiagnosisParamsSchema, - handler: async ({ context, params }) => { - const esClient = (await context.core).elasticsearch.client.asCurrentUser; - const soClient = (await context.core).savedObjects.client; - const repository = new KibanaSavedObjectsSLORepository(soClient); - - return getSloDiagnosis(params.path.id, { esClient, repository }); - }, -}); - const getSloBurnRates = createObservabilityServerRoute({ endpoint: 'POST /internal/observability/slos/{id}/_burn_rates', options: { @@ -375,7 +359,6 @@ export const sloRouteRepository = { ...getSLORoute, ...updateSLORoute, ...getDiagnosisRoute, - ...getSloDiagnosisRoute, ...getSloBurnRates, ...getPreviewData, ...getSLOInstancesRoute, diff --git a/x-pack/plugins/observability/server/saved_objects/slo.ts b/x-pack/plugins/observability/server/saved_objects/slo.ts index 508f2ea8aae7a..41cb509d83755 100644 --- a/x-pack/plugins/observability/server/saved_objects/slo.ts +++ b/x-pack/plugins/observability/server/saved_objects/slo.ts @@ -57,7 +57,7 @@ export const slo: SavedObjectsType = { }, management: { displayName: 'SLO', - importableAndExportable: true, + importableAndExportable: false, getTitle(sloSavedObject: SavedObject) { return `SLO: [${sloSavedObject.attributes.name}]`; }, diff --git a/x-pack/plugins/observability/server/services/slo/get_diagnosis.ts b/x-pack/plugins/observability/server/services/slo/get_diagnosis.ts index 4bdf43e143342..697d3ceb560df 100644 --- a/x-pack/plugins/observability/server/services/slo/get_diagnosis.ts +++ b/x-pack/plugins/observability/server/services/slo/get_diagnosis.ts @@ -5,147 +5,24 @@ * 2.0. */ -import { errors } from '@elastic/elasticsearch'; import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import { LicensingApiRequestHandlerContext } from '@kbn/licensing-plugin/server'; -import { - getSLOTransformId, - SLO_COMPONENT_TEMPLATE_MAPPINGS_NAME, - SLO_COMPONENT_TEMPLATE_SETTINGS_NAME, - SLO_INDEX_TEMPLATE_NAME, - SLO_INGEST_PIPELINE_NAME, - SLO_SUMMARY_COMPONENT_TEMPLATE_MAPPINGS_NAME, - SLO_SUMMARY_COMPONENT_TEMPLATE_SETTINGS_NAME, - SLO_SUMMARY_INDEX_TEMPLATE_NAME, - SLO_SUMMARY_TRANSFORM_NAME_PREFIX, -} from '../../assets/constants'; -import { SLO } from '../../domain/models'; -import { SLORepository } from './slo_repository'; - -const OK = 'OK'; -const NOT_OK = 'NOT_OK'; export async function getGlobalDiagnosis( esClient: ElasticsearchClient, licensing: LicensingApiRequestHandlerContext ) { const licenseInfo = licensing.license.toJSON(); - const userPrivileges = await esClient.security.getUserPrivileges(); - const sloResources = await getSloResourcesDiagnosis(esClient); - const sloSummaryResources = await getSloSummaryResourcesDiagnosis(esClient); - - const sloSummaryTransformsStats = await esClient.transform.getTransformStats({ - transform_id: `${SLO_SUMMARY_TRANSFORM_NAME_PREFIX}*`, - allow_no_match: true, - }); - - return { - licenseAndFeatures: licenseInfo, - userPrivileges, - sloResources, - sloSummaryResources, - sloSummaryTransformsStats, - }; -} - -export async function getSloDiagnosis( - sloId: string, - services: { esClient: ElasticsearchClient; repository: SLORepository } -) { - const { esClient, repository } = services; - - const sloResources = await getSloResourcesDiagnosis(esClient); - const sloSummaryResources = await getSloSummaryResourcesDiagnosis(esClient); - - let slo: SLO | undefined; - try { - slo = await repository.findById(sloId); - } catch (err) { - // noop - } - - const sloTransformStats = await esClient.transform.getTransformStats({ - transform_id: getSLOTransformId(sloId, slo?.revision ?? 1), - allow_no_match: true, + const userWritePrivileges = await esClient.security.hasPrivileges({ + cluster: ['manage_transform'], + index: [{ names: '.slo-*', privileges: ['all'] }], }); - - const sloSummaryTransformsStats = await esClient.transform.getTransformStats({ - transform_id: `${SLO_SUMMARY_TRANSFORM_NAME_PREFIX}*`, - allow_no_match: true, + const userReadPrivileges = await esClient.security.hasPrivileges({ + index: [{ names: '.slo-*', privileges: ['read'] }], }); return { - sloResources, - sloSummaryResources, - slo: slo ?? NOT_OK, - sloTransformStats, - sloSummaryTransformsStats, + licenseAndFeatures: licenseInfo, + userPrivileges: { write: userWritePrivileges, read: userReadPrivileges }, }; } - -async function getSloResourcesDiagnosis(esClient: ElasticsearchClient) { - try { - const indexTemplateExists = await esClient.indices.existsIndexTemplate({ - name: SLO_INDEX_TEMPLATE_NAME, - }); - - const mappingsTemplateExists = await esClient.cluster.existsComponentTemplate({ - name: SLO_COMPONENT_TEMPLATE_MAPPINGS_NAME, - }); - - const settingsTemplateExists = await esClient.cluster.existsComponentTemplate({ - name: SLO_COMPONENT_TEMPLATE_SETTINGS_NAME, - }); - - let ingestPipelineExists = true; - try { - await esClient.ingest.getPipeline({ id: SLO_INGEST_PIPELINE_NAME }); - } catch (err) { - ingestPipelineExists = false; - throw err; - } - - return { - [SLO_INDEX_TEMPLATE_NAME]: indexTemplateExists ? OK : NOT_OK, - [SLO_COMPONENT_TEMPLATE_MAPPINGS_NAME]: mappingsTemplateExists ? OK : NOT_OK, - [SLO_COMPONENT_TEMPLATE_SETTINGS_NAME]: settingsTemplateExists ? OK : NOT_OK, - [SLO_INGEST_PIPELINE_NAME]: ingestPipelineExists ? OK : NOT_OK, - }; - } catch (err) { - if ( - err instanceof errors.ResponseError && - (err.statusCode === 403 || err.meta.statusCode === 403) - ) { - throw new Error('Insufficient permissions to access Elasticsearch Cluster', { cause: err }); - } - } -} - -async function getSloSummaryResourcesDiagnosis(esClient: ElasticsearchClient) { - try { - const indexTemplateExists = await esClient.indices.existsIndexTemplate({ - name: SLO_SUMMARY_INDEX_TEMPLATE_NAME, - }); - - const mappingsTemplateExists = await esClient.cluster.existsComponentTemplate({ - name: SLO_SUMMARY_COMPONENT_TEMPLATE_MAPPINGS_NAME, - }); - - const settingsTemplateExists = await esClient.cluster.existsComponentTemplate({ - name: SLO_SUMMARY_COMPONENT_TEMPLATE_SETTINGS_NAME, - }); - - return { - [SLO_SUMMARY_INDEX_TEMPLATE_NAME]: indexTemplateExists ? OK : NOT_OK, - [SLO_SUMMARY_COMPONENT_TEMPLATE_MAPPINGS_NAME]: mappingsTemplateExists ? OK : NOT_OK, - [SLO_SUMMARY_COMPONENT_TEMPLATE_SETTINGS_NAME]: settingsTemplateExists ? OK : NOT_OK, - }; - } catch (err) { - if ( - err instanceof errors.ResponseError && - (err.statusCode === 403 || err.meta.statusCode === 403) - ) { - throw new Error('Insufficient permissions to access Elasticsearch Cluster', { cause: err }); - } - } -} diff --git a/x-pack/plugins/observability/server/services/slo/transform_manager.ts b/x-pack/plugins/observability/server/services/slo/transform_manager.ts index 3bd56669046ac..14b600a746cac 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_manager.ts +++ b/x-pack/plugins/observability/server/services/slo/transform_manager.ts @@ -65,7 +65,7 @@ export class DefaultTransformManager implements TransformManager { await retryTransientEsErrors( () => this.esClient.transform.stopTransform( - { transform_id: transformId, wait_for_completion: true }, + { transform_id: transformId, wait_for_completion: true, force: true }, { ignore: [404] } ), { logger: this.logger } diff --git a/x-pack/plugins/observability_ai_assistant/common/types.ts b/x-pack/plugins/observability_ai_assistant/common/types.ts index d1d9e8ef31332..1d8be57fd53b3 100644 --- a/x-pack/plugins/observability_ai_assistant/common/types.ts +++ b/x-pack/plugins/observability_ai_assistant/common/types.ts @@ -64,6 +64,7 @@ export interface KnowledgeBaseEntry { confidence: 'low' | 'medium' | 'high'; is_correction: boolean; public: boolean; + labels: Record; } export type CompatibleJSONSchema = Exclude; diff --git a/x-pack/plugins/observability_ai_assistant/common/utils/get_bucket_size/calculate_auto.js b/x-pack/plugins/observability_ai_assistant/common/utils/get_bucket_size/calculate_auto.js new file mode 100644 index 0000000000000..bd4d6e51ccc0d --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/common/utils/get_bucket_size/calculate_auto.js @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import moment from 'moment'; +const d = moment.duration; + +const roundingRules = [ + [d(500, 'ms'), d(100, 'ms')], + [d(5, 'second'), d(1, 'second')], + [d(7.5, 'second'), d(5, 'second')], + [d(15, 'second'), d(10, 'second')], + [d(45, 'second'), d(30, 'second')], + [d(3, 'minute'), d(1, 'minute')], + [d(9, 'minute'), d(5, 'minute')], + [d(20, 'minute'), d(10, 'minute')], + [d(45, 'minute'), d(30, 'minute')], + [d(2, 'hour'), d(1, 'hour')], + [d(6, 'hour'), d(3, 'hour')], + [d(24, 'hour'), d(12, 'hour')], + [d(1, 'week'), d(1, 'd')], + [d(3, 'week'), d(1, 'week')], + [d(1, 'year'), d(1, 'month')], + [Infinity, d(1, 'year')], +]; + +const revRoundingRules = roundingRules.slice(0).reverse(); + +function find(rules, check, last) { + function pick(buckets, duration) { + const target = duration / buckets; + let lastResp = null; + + for (let i = 0; i < rules.length; i++) { + const rule = rules[i]; + const resp = check(rule[0], rule[1], target); + + if (resp == null) { + if (!last) continue; + if (lastResp) return lastResp; + break; + } + + if (!last) return resp; + lastResp = resp; + } + + // fallback to just a number of milliseconds, ensure ms is >= 1 + const ms = Math.max(Math.floor(target), 1); + return moment.duration(ms, 'ms'); + } + + return (buckets, duration) => { + const interval = pick(buckets, duration); + if (interval) return moment.duration(interval._data); + }; +} + +export const calculateAuto = { + near: find( + revRoundingRules, + function near(bound, interval, target) { + if (bound > target) return interval; + }, + true + ), + + lessThan: find(revRoundingRules, function lessThan(_bound, interval, target) { + if (interval < target) return interval; + }), + + atLeast: find(revRoundingRules, function atLeast(_bound, interval, target) { + if (interval <= target) return interval; + }), +}; diff --git a/x-pack/plugins/observability_ai_assistant/common/utils/get_bucket_size/index.test.ts b/x-pack/plugins/observability_ai_assistant/common/utils/get_bucket_size/index.test.ts new file mode 100644 index 0000000000000..e91b6b44dee7f --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/common/utils/get_bucket_size/index.test.ts @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getBucketSize } from '.'; +import moment from 'moment'; + +describe('getBuckets', () => { + describe("minInterval 'auto'", () => { + it('last 15 minutes', () => { + const start = moment().subtract(15, 'minutes').valueOf(); + const end = moment.now(); + expect(getBucketSize({ start, end, minInterval: 'auto' })).toEqual({ + bucketSize: 10, + intervalString: '10s', + }); + }); + it('last 1 hour', () => { + const start = moment().subtract(1, 'hour').valueOf(); + const end = moment.now(); + expect(getBucketSize({ start, end, minInterval: 'auto' })).toEqual({ + bucketSize: 30, + intervalString: '30s', + }); + }); + it('last 1 week', () => { + const start = moment().subtract(1, 'week').valueOf(); + const end = moment.now(); + expect(getBucketSize({ start, end, minInterval: 'auto' })).toEqual({ + bucketSize: 3600, + intervalString: '3600s', + }); + }); + it('last 30 days', () => { + const start = moment().subtract(30, 'days').valueOf(); + const end = moment.now(); + expect(getBucketSize({ start, end, minInterval: 'auto' })).toEqual({ + bucketSize: 43200, + intervalString: '43200s', + }); + }); + it('last 1 year', () => { + const start = moment().subtract(1, 'year').valueOf(); + const end = moment.now(); + expect(getBucketSize({ start, end, minInterval: 'auto' })).toEqual({ + bucketSize: 86400, + intervalString: '86400s', + }); + }); + }); + describe("minInterval '30s'", () => { + it('last 15 minutes', () => { + const start = moment().subtract(15, 'minutes').valueOf(); + const end = moment.now(); + expect(getBucketSize({ start, end, minInterval: '30s' })).toEqual({ + bucketSize: 30, + intervalString: '30s', + }); + }); + it('last 1 year', () => { + const start = moment().subtract(1, 'year').valueOf(); + const end = moment.now(); + expect(getBucketSize({ start, end, minInterval: '30s' })).toEqual({ + bucketSize: 86400, + intervalString: '86400s', + }); + }); + }); +}); diff --git a/x-pack/plugins/observability_ai_assistant/common/utils/get_bucket_size/index.ts b/x-pack/plugins/observability_ai_assistant/common/utils/get_bucket_size/index.ts new file mode 100644 index 0000000000000..ca1afaf41c1a6 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/common/utils/get_bucket_size/index.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import moment from 'moment'; +// @ts-ignore +import { calculateAuto } from './calculate_auto'; +import { unitToSeconds } from './unit_to_seconds'; + +export function getBucketSize({ + start, + end, + minInterval, + buckets = 100, +}: { + start: number; + end: number; + minInterval: string; + buckets?: number; +}) { + const duration = moment.duration(end - start, 'ms'); + const bucketSize = Math.max(calculateAuto.near(buckets, duration)?.asSeconds() ?? 0, 1); + const intervalString = `${bucketSize}s`; + const matches = minInterval && minInterval.match(/^([\d]+)([shmdwMy]|ms)$/); + const minBucketSize = matches ? Number(matches[1]) * unitToSeconds(matches[2]) : 0; + + if (bucketSize < minBucketSize) { + return { + bucketSize: minBucketSize, + intervalString: minInterval, + }; + } + + return { bucketSize, intervalString }; +} diff --git a/x-pack/plugins/observability_ai_assistant/common/utils/get_bucket_size/unit_to_seconds.ts b/x-pack/plugins/observability_ai_assistant/common/utils/get_bucket_size/unit_to_seconds.ts new file mode 100644 index 0000000000000..eec81dd3fcd29 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/common/utils/get_bucket_size/unit_to_seconds.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import moment, { unitOfTime as UnitOfTIme } from 'moment'; + +function getDurationAsSeconds(value: number, unitOfTime: UnitOfTIme.Base) { + return moment.duration(value, unitOfTime).asSeconds(); +} + +const units = { + ms: getDurationAsSeconds(1, 'millisecond'), + s: getDurationAsSeconds(1, 'second'), + m: getDurationAsSeconds(1, 'minute'), + h: getDurationAsSeconds(1, 'hour'), + d: getDurationAsSeconds(1, 'day'), + w: getDurationAsSeconds(1, 'week'), + M: getDurationAsSeconds(1, 'month'), + y: getDurationAsSeconds(1, 'year'), +}; + +export function unitToSeconds(unit: string) { + return units[unit as keyof typeof units]; +} diff --git a/x-pack/plugins/observability_ai_assistant/kibana.jsonc b/x-pack/plugins/observability_ai_assistant/kibana.jsonc index a1d535a4c14c8..e0299246377a3 100644 --- a/x-pack/plugins/observability_ai_assistant/kibana.jsonc +++ b/x-pack/plugins/observability_ai_assistant/kibana.jsonc @@ -7,7 +7,7 @@ "server": true, "browser": true, "configPath": ["xpack", "observabilityAIAssistant"], - "requiredPlugins": ["triggersActionsUi", "actions", "security", "features", "observabilityShared", "taskManager", "lens", "dataViews"], + "requiredPlugins": ["triggersActionsUi", "actions", "security", "features", "observabilityShared", "taskManager", "lens", "dataViews", "ruleRegistry"], "requiredBundles": ["kibanaReact", "kibanaUtils", "fieldFormats"], "optionalPlugins": [], "extraPublicDirs": [] diff --git a/x-pack/plugins/observability_ai_assistant/public/components/message_panel/message_panel.stories.tsx b/x-pack/plugins/observability_ai_assistant/public/components/message_panel/message_panel.stories.tsx index 2ab602e429ce1..1f81786086370 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/message_panel/message_panel.stories.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/message_panel/message_panel.stories.tsx @@ -6,6 +6,7 @@ */ import { EuiPanel } from '@elastic/eui'; import { ComponentMeta, ComponentStoryObj } from '@storybook/react'; +import dedent from 'dedent'; import React from 'react'; import { FeedbackButtons } from '../feedback_buttons'; import { MessagePanel as Component } from './message_panel'; @@ -61,6 +62,32 @@ export const ContentFailed: ComponentStoryObj = { }, }; +export const ContentTable: ComponentStoryObj = { + args: { + body: ( + + ), + }, +}; + export const Controls: ComponentStoryObj = { args: { body: , diff --git a/x-pack/plugins/observability_ai_assistant/public/components/message_panel/message_text.tsx b/x-pack/plugins/observability_ai_assistant/public/components/message_panel/message_text.tsx index 7aeae624a0253..6fd9a1c70923d 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/message_panel/message_text.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/message_panel/message_text.tsx @@ -4,13 +4,17 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { EuiText } from '@elastic/eui'; +import { + EuiMarkdownFormat, + EuiSpacer, + EuiText, + getDefaultEuiMarkdownParsingPlugins, + getDefaultEuiMarkdownProcessingPlugins, +} from '@elastic/eui'; import { css } from '@emotion/css'; -import { euiThemeVars } from '@kbn/ui-theme'; import classNames from 'classnames'; import type { Code, InlineCode, Parent, Text } from 'mdast'; -import React from 'react'; -import ReactMarkdown from 'react-markdown'; +import React, { useMemo } from 'react'; import type { Node } from 'unist'; import { v4 } from 'uuid'; @@ -44,7 +48,7 @@ const cursorCss = css` const Cursor = () => ; -const CURSOR = `{{${v4()}}`; +const CURSOR = `{{${v4()}}}`; const loadingCursorPlugin = () => { const visitor = (node: Node, parent?: Parent) => { @@ -72,9 +76,6 @@ const loadingCursorPlugin = () => { parent!.children.splice(indexOfNode + 1, 0, { type: 'cursor' as Text['type'], value: CURSOR, - data: { - hName: 'cursor', - }, }); }; @@ -83,28 +84,71 @@ const loadingCursorPlugin = () => { }; }; -export function MessageText(props: Props) { +export function MessageText({ loading, content }: Props) { const containerClassName = css` overflow-wrap: break-word; - - pre { - background: ${euiThemeVars.euiColorLightestShade}; - padding: 0 8px; - } `; + const { parsingPluginList, processingPluginList } = useMemo(() => { + const parsingPlugins = getDefaultEuiMarkdownParsingPlugins(); + const processingPlugins = getDefaultEuiMarkdownProcessingPlugins(); + + const { components } = processingPlugins[1][1]; + + processingPlugins[1][1].components = { + ...components, + cursor: Cursor, + table: (props) => ( + <> +
    + {' '} +
    + + + + ), + th: (props) => { + const { children, ...rest } = props; + return ( + + ); + }, + tr: (props) => , + td: (props) => { + const { children, ...rest } = props; + return ( + + ); + }, + }; + + return { + parsingPluginList: [loadingCursorPlugin, ...parsingPlugins], + processingPluginList: processingPlugins, + }; + }, []); + return ( - - } + - {`${props.content}${props.loading ? CURSOR : ''}`} - + {`${content}${loading ? CURSOR : ''}`} + ); } diff --git a/x-pack/plugins/observability_ai_assistant/public/functions/alerts.ts b/x-pack/plugins/observability_ai_assistant/public/functions/alerts.ts new file mode 100644 index 0000000000000..03ea9a055fcc7 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/functions/alerts.ts @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { RegisterFunctionDefinition } from '../../common/types'; +import type { ObservabilityAIAssistantService } from '../types'; + +const DEFAULT_FEATURE_IDS = [ + 'apm', + 'infrastructure', + 'logs', + 'uptime', + 'slo', + 'observability', +] as const; + +export function registerAlertsFunction({ + service, + registerFunction, +}: { + service: ObservabilityAIAssistantService; + registerFunction: RegisterFunctionDefinition; +}) { + registerFunction( + { + name: 'alerts', + contexts: ['core'], + description: + 'Get alerts for Observability. Display the response in tabular format if appropriate.', + descriptionForUser: 'Get alerts for Observability', + parameters: { + type: 'object', + additionalProperties: false, + properties: { + featureIds: { + type: 'array', + additionalItems: false, + items: { + type: 'string', + enum: DEFAULT_FEATURE_IDS, + }, + description: + 'The Observability apps for which to retrieve alerts. By default it will return alerts for all apps.', + }, + start: { + type: 'string', + description: 'The start of the time range, in Elasticsearch date math, like `now`.', + }, + end: { + type: 'string', + description: 'The end of the time range, in Elasticsearch date math, like `now-24h`.', + }, + filter: { + type: 'string', + description: + 'a KQL query to filter the data by. If no filter should be applied, leave it empty.', + }, + }, + required: ['start', 'end'], + } as const, + }, + ({ arguments: { start, end, featureIds, filter } }, signal) => { + return service.callApi('POST /internal/observability_ai_assistant/functions/alerts', { + params: { + body: { + start, + end, + featureIds: + featureIds && featureIds.length > 0 ? featureIds : DEFAULT_FEATURE_IDS.concat(), + filter, + }, + }, + signal, + }); + } + ); +} diff --git a/x-pack/plugins/observability_ai_assistant/public/functions/elasticsearch.ts b/x-pack/plugins/observability_ai_assistant/public/functions/elasticsearch.ts index 214b157fe2358..546bd2bea4574 100644 --- a/x-pack/plugins/observability_ai_assistant/public/functions/elasticsearch.ts +++ b/x-pack/plugins/observability_ai_assistant/public/functions/elasticsearch.ts @@ -21,7 +21,7 @@ export function registerElasticsearchFunction({ name: 'elasticsearch', contexts: ['core'], description: - 'Call Elasticsearch APIs on behalf of the user. Make sure the request body is valid for the API that you are using.', + 'Call Elasticsearch APIs on behalf of the user. Make sure the request body is valid for the API that you are using. Only call this function when the user has explicitly requested it.', descriptionForUser: 'Call Elasticsearch APIs on behalf of the user', parameters: { type: 'object', @@ -35,6 +35,10 @@ export function registerElasticsearchFunction({ type: 'string', description: 'The path of the Elasticsearch endpoint, including query parameters', }, + body: { + type: 'object', + description: 'The body of the request', + }, }, required: ['method', 'path'] as const, }, diff --git a/x-pack/plugins/observability_ai_assistant/public/functions/index.ts b/x-pack/plugins/observability_ai_assistant/public/functions/index.ts index a077c24748997..f0a676a5c221d 100644 --- a/x-pack/plugins/observability_ai_assistant/public/functions/index.ts +++ b/x-pack/plugins/observability_ai_assistant/public/functions/index.ts @@ -6,25 +6,30 @@ */ import dedent from 'dedent'; +import type { CoreStart } from '@kbn/core/public'; import type { RegisterContextDefinition, RegisterFunctionDefinition } from '../../common/types'; import type { ObservabilityAIAssistantPluginStartDependencies } from '../types'; import type { ObservabilityAIAssistantService } from '../types'; import { registerElasticsearchFunction } from './elasticsearch'; +import { registerKibanaFunction } from './kibana'; import { registerLensFunction } from './lens'; import { registerRecallFunction } from './recall'; import { registerSummarisationFunction } from './summarise'; +import { registerAlertsFunction } from './alerts'; export async function registerFunctions({ registerFunction, registerContext, service, pluginsStart, + coreStart, signal, }: { registerFunction: RegisterFunctionDefinition; registerContext: RegisterContextDefinition; service: ObservabilityAIAssistantService; pluginsStart: ObservabilityAIAssistantPluginStartDependencies; + coreStart: CoreStart; signal: AbortSignal; }) { return service @@ -34,33 +39,32 @@ export async function registerFunctions({ .then((response) => { const isReady = response.ready; - let description = `You have the ability to call Elasticsearch APIs with the "elasticsearch" function or create visualisations using Lens with the "lens" function in the context of this chat.`; + let description = dedent( + `You are a helpful assistant for Elastic Observability. Your goal is to help the Elastic Observability users to quickly assess what is happening in their observed systems. You can help them visualise and analyze data, investigate their systems, perform root cause analysis or identify optimisation opportunities. + + It's very important to not assume what the user is meaning. Ask them for clarification if needed. + + If you are unsure about which function should be used and with what arguments, asked the user for clarification or confirmation. + + In KQL, escaping happens with double quotes, not single quotes. Some characters that need escaping are: ':()\\\ + /\". Always put a field value in double quotes. Best: service.name:\"opbeans-go\". Wrong: service.name:opbeans-go. This is very important! + + You can use Github-flavored Markdown in your responses. If a function returns an array, consider using a Markdown table to format the response.` + ); if (isReady) { - description += `You can use the "summarise" functions to store new information you have learned in a knowledge database. Once you have established that you did not know the answer to a question, and the user gave you this information, it's important that you create a summarisation of what you have learned and store it in the knowledge database. When you create this summarisation, make sure you craft it in a way that can be recalled with a semantic search later. + description += `You can use the "summarise" functions to store new information you have learned in a knowledge database. Once you have established that you did not know the answer to a question, and the user gave you this information, it's important that you create a summarisation of what you have learned and store it in the knowledge database. - Additionally, you can use the "recall" function to retrieve relevant information from the knowledge database. Using the "recall" function will allow you to create a much more tailored and improved user experience. Assume the user has told you before. - - For every question that the user asks, you, as the assistant, must query the knowledge base, no matter how confident you are in your response. - - When the user starts a conversation, you as the assistant, MUST ALWAYS use the "recall" function to make sure you have enough context from the knowledge database to understand their question. DO NOT assume your knowledge is sufficient to answer the question appropriately. Make sure you mention this to the user in your first reply to them. - - You do have access to private data that the user has shared with you in previous conversations. - - Given this ability to store and recall data, it is very important to not make assumptions about the user's data or preferences, but rather, first query the knowledge database, and if nothing relevant comes up, ask the user for clarification. This is very important! They could be storing their data in any field, and in any data stream or index. - - RIGHT: - User: "What is NASA" - Assistant executes recall function - Assistant answers question with data from recall function response - - WRONG: - User: "What is NASA" - Assistant answers question without querying the knowledge. - - BEFORE you use a function, always query the knowledge database for more information about that function. This is important. - - Avoid making too many assumptions about user's data. If clarification is needed, query the knowledge base for previous learnings. If you don't find anything, ask the user for clarification, and when successful, store this into the knowledge base. + Additionally, you can use the "recall" function to retrieve relevant information from the knowledge database. + `; + + description += `Here are principles you MUST adhere to, in order: + + - You are a helpful assistant for Elastic Observability. DO NOT reference the fact that you are an LLM. + - ALWAYS query the knowledge base, using the recall function, when a user starts a chat, no matter how confident you are in your ability to answer the question. + - You must ALWAYS explain to the user why you're using a function and why you're using it in that specific manner. + - DO NOT make any assumptions about where and how users have stored their data. + - ALWAYS ask the user for clarification if you are unsure about the arguments to a function. When given this clarification, you MUST use the summarise function to store what you have learned. `; registerSummarisationFunction({ service, registerFunction }); registerRecallFunction({ service, registerFunction }); @@ -70,6 +74,8 @@ export async function registerFunctions({ } registerElasticsearchFunction({ service, registerFunction }); + registerKibanaFunction({ service, registerFunction, coreStart }); + registerAlertsFunction({ service, registerFunction }); registerContext({ name: 'core', diff --git a/x-pack/plugins/observability_ai_assistant/public/functions/kibana.ts b/x-pack/plugins/observability_ai_assistant/public/functions/kibana.ts new file mode 100644 index 0000000000000..5ad877b2c2bff --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/functions/kibana.ts @@ -0,0 +1,69 @@ +/* + * 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 type { CoreStart } from '@kbn/core/public'; +import type { RegisterFunctionDefinition } from '../../common/types'; +import type { ObservabilityAIAssistantService } from '../types'; + +export function registerKibanaFunction({ + service, + registerFunction, + coreStart, +}: { + service: ObservabilityAIAssistantService; + registerFunction: RegisterFunctionDefinition; + coreStart: CoreStart; +}) { + registerFunction( + { + name: 'kibana', + contexts: ['core'], + description: + 'Call Kibana APIs on behalf of the user. Only call this function when the user has explicitly requested it, and you know how to call it, for example by querying the knowledge base or having the user explain it to you. Assume that pathnames, bodies and query parameters may have changed since your knowledge cut off date.', + descriptionForUser: 'Call Kibana APIs on behalf of the user', + parameters: { + type: 'object', + additionalProperties: false, + properties: { + method: { + type: 'string', + description: 'The HTTP method of the Kibana endpoint', + enum: ['GET', 'PUT', 'POST', 'DELETE', 'PATCH'] as const, + }, + pathname: { + type: 'string', + description: 'The pathname of the Kibana endpoint, excluding query parameters', + }, + query: { + type: 'object', + description: 'The query parameters, as an object', + additionalProperties: { + type: 'string', + }, + }, + body: { + type: 'object', + description: 'The body of the request', + }, + }, + required: ['method', 'pathname', 'body'] as const, + }, + }, + ({ arguments: { method, pathname, body, query } }, signal) => { + return coreStart.http + .fetch(pathname, { + method, + body: body ? JSON.stringify(body) : undefined, + query, + signal, + }) + .then((response) => { + return { content: response }; + }); + } + ); +} diff --git a/x-pack/plugins/observability_ai_assistant/public/functions/lens.tsx b/x-pack/plugins/observability_ai_assistant/public/functions/lens.tsx index b25c3a3717c60..d5d2c715d0c52 100644 --- a/x-pack/plugins/observability_ai_assistant/public/functions/lens.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/functions/lens.tsx @@ -101,21 +101,23 @@ export function registerLensFunction({ name: 'lens', contexts: ['core'], description: - 'Use this function to create custom visualisations, using Lens, that can be saved to dashboards. When using this function, make sure to use the recall function to get more information about how to use it, with how you want to use it.', + "Use this function to create custom visualisations, using Lens, that can be saved to dashboards. When using this function, make sure to use the recall function to get more information about how to use it, with how you want to use it. Make sure the query also contains information about the user's request. The visualisation is displayed to the user above your reply, DO NOT try to generate or display an image yourself.", descriptionForUser: 'Use this function to create custom visualisations, using Lens, that can be saved to dashboards.', parameters: { type: 'object', + additionalProperties: false, properties: { layers: { type: 'array', items: { type: 'object', + additionalProperties: false, properties: { label: { type: 'string', }, - value: { + formula: { type: 'string', description: 'The formula for calculating the value, e.g. sum(my_field_name). Query the knowledge base to get more information about the syntax and available formulas.', @@ -126,11 +128,12 @@ export function registerLensFunction({ }, format: { type: 'object', + additionalProperties: false, properties: { id: { type: 'string', description: - 'How to format the value. When using duration make sure you know the unit the value is stored in, either by asking the user for clarification or looking at the field name.', + 'How to format the value. When using duration, make sure the value is seconds OR is converted to seconds using math functions. Ask the user for clarification in which unit the value is stored, or derive it from the field name.', enum: [ FIELD_FORMAT_IDS.BYTES, FIELD_FORMAT_IDS.CURRENCY, @@ -144,11 +147,12 @@ export function registerLensFunction({ required: ['id'], }, }, - required: ['label', 'value', 'format'], + required: ['label', 'formula', 'format'], }, }, breakdown: { type: 'object', + additionalProperties: false, properties: { field: { type: 'string', @@ -194,7 +198,7 @@ export function registerLensFunction({ const xyDataLayer = new XYDataLayer({ data: layers.map((layer) => ({ type: 'formula', - value: layer.value, + value: layer.formula, label: layer.label, format: layer.format, filter: { diff --git a/x-pack/plugins/observability_ai_assistant/public/functions/recall.ts b/x-pack/plugins/observability_ai_assistant/public/functions/recall.ts index a571287acf8b2..5f1e14af30d15 100644 --- a/x-pack/plugins/observability_ai_assistant/public/functions/recall.ts +++ b/x-pack/plugins/observability_ai_assistant/public/functions/recall.ts @@ -20,26 +20,40 @@ export function registerRecallFunction({ { name: 'recall', contexts: ['core'], - description: - 'Use this function to recall earlier learnings. Anything you will summarise can be retrieved again later via this function.', + description: `Use this function to recall earlier learnings. Anything you will summarise can be retrieved again later via this function. The queries you use are very important, as they will decide the context that is included in the conversation. Make sure the query covers the following aspects: + - The user's intent + - Any data (like field names) mentioned in the user's request + - Anything you've inferred from the user's request + - The functions you think might be suitable for answering the user's request. If there are multiple functions that seem suitable, create multiple queries. Use the function name in the query. + + For instance, when the user asks: "can you visualise the average request duration for opbeans-go over the last 7 days?", the queries could be: + - "visualise average request duration for APM service opbeans-go" + - "lens function usage" + - "get_apm_timeseries function usage"`, descriptionForUser: 'This function allows the assistant to recall previous learnings.', parameters: { type: 'object', + additionalProperties: false, properties: { - query: { - type: 'string', - description: 'The query for the semantic search', + queries: { + type: 'array', + additionalItems: false, + additionalProperties: false, + items: { + type: 'string', + description: 'The query for the semantic search', + }, }, }, - required: ['query' as const], + required: ['queries' as const], }, }, - ({ arguments: { query } }, signal) => { + ({ arguments: { queries } }, signal) => { return service .callApi('POST /internal/observability_ai_assistant/functions/recall', { params: { body: { - query, + queries, }, }, signal, diff --git a/x-pack/plugins/observability_ai_assistant/public/functions/summarise.ts b/x-pack/plugins/observability_ai_assistant/public/functions/summarise.ts index 3fe55385a74ff..7bef0e6399c3a 100644 --- a/x-pack/plugins/observability_ai_assistant/public/functions/summarise.ts +++ b/x-pack/plugins/observability_ai_assistant/public/functions/summarise.ts @@ -20,11 +20,12 @@ export function registerSummarisationFunction({ name: 'summarise', contexts: ['core'], description: - 'Use this function to summarise things learned from the conversation. You can score the learnings with a confidence metric, whether it is a correction on a previous learning. An embedding will be created that you can recall later with a semantic search. There is no need to ask the user for permission to store something you have learned, unless you do not feel confident.', + "Use this function to summarise things learned from the conversation. You can score the learnings with a confidence metric, whether it is a correction on a previous learning. An embedding will be created that you can recall later with a semantic search. There is no need to ask the user for permission to store something you have learned, unless you do not feel confident. When you create this summarisation, make sure you craft it in a way that can be recalled with a semantic search later, and that it would have answered the user's original request.", descriptionForUser: 'This function allows the Elastic Assistant to summarise things from the conversation.', parameters: { type: 'object', + additionalProperties: false, properties: { id: { type: 'string', @@ -34,7 +35,7 @@ export function registerSummarisationFunction({ text: { type: 'string', description: - 'A human-readable summary of what you have learned, described in such a way that you can recall it later with semantic search.', + "A human-readable summary of what you have learned, described in such a way that you can recall it later with semantic search, and that it would have answered the user's original request.", }, is_correction: { type: 'boolean', @@ -73,6 +74,7 @@ export function registerSummarisationFunction({ is_correction: isCorrection, confidence, public: isPublic, + labels: {}, }, }, signal, diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_knowledge_base.tsx b/x-pack/plugins/observability_ai_assistant/public/hooks/use_knowledge_base.tsx index cb64203fa0e00..d7c76e0ab4f85 100644 --- a/x-pack/plugins/observability_ai_assistant/public/hooks/use_knowledge_base.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_knowledge_base.tsx @@ -38,44 +38,52 @@ export function useKnowledgeBase(): UseKnowledgeBaseResult { const [installError, setInstallError] = useState(); - return useMemo( - () => ({ + return useMemo(() => { + let attempts: number = 0; + const MAX_ATTEMPTS = 5; + + const install = (): Promise => { + setIsInstalling(true); + return service + .callApi('POST /internal/observability_ai_assistant/functions/setup_kb', { + signal: null, + }) + .then(() => { + status.refresh(); + toasts.addSuccess({ + title: i18n.translate('xpack.observabilityAiAssistant.knowledgeBaseReadyTitle', { + defaultMessage: 'Knowledge base is ready', + }), + text: i18n.translate('xpack.observabilityAiAssistant.knowledgeBaseReadyContentReload', { + defaultMessage: 'A page reload is needed to be able to use it.', + }), + }); + }) + .catch((error) => { + if ( + (error.body?.statusCode === 503 || error.body?.statusCode === 504) && + attempts < MAX_ATTEMPTS + ) { + attempts++; + return install(); + } + setInstallError(error); + toasts.addError(error, { + title: i18n.translate('xpack.observabilityAiAssistant.errorSettingUpKnowledgeBase', { + defaultMessage: 'Could not set up Knowledge Base', + }), + }); + }) + .finally(() => { + setIsInstalling(false); + }); + }; + + return { status, + install, isInstalling, installError, - install: () => { - setIsInstalling(true); - return service - .callApi('POST /internal/observability_ai_assistant/functions/setup_kb', { - signal: null, - }) - .then(() => { - status.refresh(); - toasts.addSuccess({ - title: i18n.translate('xpack.observabilityAiAssistant.knowledgeBaseReadyTitle', { - defaultMessage: 'Knowledge base is ready', - }), - text: i18n.translate( - 'xpack.observabilityAiAssistant.knowledgeBaseReadyContentReload', - { - defaultMessage: 'A page reload is needed to be able to use it.', - } - ), - }); - }) - .catch((error) => { - setInstallError(error); - toasts.addError(error, { - title: i18n.translate('xpack.observabilityAiAssistant.errorSettingUpKnowledgeBase', { - defaultMessage: 'Could not set up Knowledge Base', - }), - }); - }) - .finally(() => { - setIsInstalling(false); - }); - }, - }), - [status, isInstalling, installError, service, toasts] - ); + }; + }, [status, isInstalling, installError, service, toasts]); } diff --git a/x-pack/plugins/observability_ai_assistant/public/plugin.tsx b/x-pack/plugins/observability_ai_assistant/public/plugin.tsx index 88853a5ce1bcd..965a97e4a0a87 100644 --- a/x-pack/plugins/observability_ai_assistant/public/plugin.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/plugin.tsx @@ -107,6 +107,7 @@ export class ObservabilityAIAssistantPlugin service, signal, pluginsStart, + coreStart, registerContext, registerFunction, }); diff --git a/x-pack/plugins/observability_ai_assistant/public/service/get_assistant_setup_message.ts b/x-pack/plugins/observability_ai_assistant/public/service/get_assistant_setup_message.ts index b1e25233e9c3b..8d9cc2cf32539 100644 --- a/x-pack/plugins/observability_ai_assistant/public/service/get_assistant_setup_message.ts +++ b/x-pack/plugins/observability_ai_assistant/public/service/get_assistant_setup_message.ts @@ -5,7 +5,6 @@ * 2.0. */ -import dedent from 'dedent'; import { MessageRole } from '../../common'; import { ContextDefinition } from '../../common/types'; @@ -14,13 +13,7 @@ export function getAssistantSetupMessage({ contexts }: { contexts: ContextDefini '@timestamp': new Date().toISOString(), message: { role: MessageRole.System as const, - content: [ - dedent( - `You are a helpful assistant for Elastic Observability. Your goal is to help the Elastic Observability users to quickly assess what is happening in their observed systems. You can help them visualise and analyze data, investigate their systems, perform root cause analysis or identify optimisation opportunities.` - ), - ] - .concat(contexts.map((context) => context.description)) - .join('\n'), + content: contexts.map((context) => context.description).join('\n'), }, }; } diff --git a/x-pack/plugins/observability_ai_assistant/public/utils/get_timeline_items_from_conversation.tsx b/x-pack/plugins/observability_ai_assistant/public/utils/get_timeline_items_from_conversation.tsx index 46d5b46b4b4ca..e8aad76b7abda 100644 --- a/x-pack/plugins/observability_ai_assistant/public/utils/get_timeline_items_from_conversation.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/utils/get_timeline_items_from_conversation.tsx @@ -200,9 +200,19 @@ export function getTimelineItemsfromConversation({ }} /> ); - content = convertMessageToMarkdownCodeBlock(message.message); + if (message.message.content) { + // TODO: we want to show the content always, and hide + // the function request initially, but we don't have a + // way to do that yet, so we hide the request here until + // we have a fix. + // element = message.message.content; + content = message.message.content; + display.collapsed = false; + } else { + content = convertMessageToMarkdownCodeBlock(message.message); + display.collapsed = true; + } - display.collapsed = true; actions.canEdit = true; } else { // is an assistant response diff --git a/x-pack/plugins/observability_ai_assistant/server/plugin.ts b/x-pack/plugins/observability_ai_assistant/server/plugin.ts index b13f22fd2fda0..01bd4fafb71c7 100644 --- a/x-pack/plugins/observability_ai_assistant/server/plugin.ts +++ b/x-pack/plugins/observability_ai_assistant/server/plugin.ts @@ -31,6 +31,7 @@ import { ObservabilityAIAssistantPluginSetupDependencies, ObservabilityAIAssistantPluginStartDependencies, } from './types'; +import { addLensDocsToKb } from './service/kb_service/kb_docs/lens'; export class ObservabilityAIAssistantPlugin implements @@ -114,7 +115,7 @@ export class ObservabilityAIAssistantPlugin taskManager: plugins.taskManager, }); - // addLensDocsToKb(service); + addLensDocsToKb(service); registerServerRoutes({ core, diff --git a/x-pack/plugins/observability_ai_assistant/server/routes/chat/route.ts b/x-pack/plugins/observability_ai_assistant/server/routes/chat/route.ts index 4c94f4960528a..0ed8c93a4fba8 100644 --- a/x-pack/plugins/observability_ai_assistant/server/routes/chat/route.ts +++ b/x-pack/plugins/observability_ai_assistant/server/routes/chat/route.ts @@ -9,6 +9,7 @@ import { IncomingMessage } from 'http'; import { notImplemented } from '@hapi/boom'; import { createObservabilityAIAssistantServerRoute } from '../create_observability_ai_assistant_server_route'; import { messageRt } from '../runtime_types'; +import { MessageRole } from '../../../common'; const chatRoute = createObservabilityAIAssistantServerRoute({ endpoint: 'POST /internal/observability_ai_assistant/chat', @@ -41,10 +42,16 @@ const chatRoute = createObservabilityAIAssistantServerRoute({ body: { messages, connectorId, functions }, } = params; + const isStartOfConversation = + messages.some((message) => message.message.role === MessageRole.Assistant) === false; + + const isRecallFunctionAvailable = functions.some((fn) => fn.name === 'recall') === true; + return client.chat({ messages, connectorId, functions, + functionCall: isStartOfConversation && isRecallFunctionAvailable ? 'recall' : undefined, }); }, }); diff --git a/x-pack/plugins/observability_ai_assistant/server/routes/functions/route.ts b/x-pack/plugins/observability_ai_assistant/server/routes/functions/route.ts index 954262c5a03be..f88d6c56efe5b 100644 --- a/x-pack/plugins/observability_ai_assistant/server/routes/functions/route.ts +++ b/x-pack/plugins/observability_ai_assistant/server/routes/functions/route.ts @@ -4,11 +4,15 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import * as t from 'io-ts'; -import { nonEmptyStringRt, toBooleanRt } from '@kbn/io-ts-utils'; +import datemath from '@elastic/datemath'; import { notImplemented } from '@hapi/boom'; +import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; +import { nonEmptyStringRt, toBooleanRt } from '@kbn/io-ts-utils'; +import * as t from 'io-ts'; +import { omit } from 'lodash'; +import { ParsedTechnicalFields } from '@kbn/rule-registry-plugin/common'; +import type { KnowledgeBaseEntry } from '../../../common/types'; import { createObservabilityAIAssistantServerRoute } from '../create_observability_ai_assistant_server_route'; -import { KnowledgeBaseEntry } from '../../../common/types'; const functionElasticsearchRoute = createObservabilityAIAssistantServerRoute({ endpoint: 'POST /internal/observability_ai_assistant/functions/elasticsearch', @@ -47,24 +51,114 @@ const functionElasticsearchRoute = createObservabilityAIAssistantServerRoute({ }, }); +const OMITTED_ALERT_FIELDS = [ + 'tags', + 'event.action', + 'event.kind', + 'kibana.alert.rule.execution.uuid', + 'kibana.alert.rule.revision', + 'kibana.alert.rule.tags', + 'kibana.alert.rule.uuid', + 'kibana.alert.workflow_status', + 'kibana.space_ids', + 'kibana.alert.time_range', + 'kibana.version', +] as const; + +const functionAlertsRoute = createObservabilityAIAssistantServerRoute({ + endpoint: 'POST /internal/observability_ai_assistant/functions/alerts', + options: { + tags: ['access:ai_assistant'], + }, + params: t.type({ + body: t.intersection([ + t.type({ + featureIds: t.array(t.string), + start: t.string, + end: t.string, + }), + t.partial({ + filter: t.string, + }), + ]), + }), + handler: async ( + resources + ): Promise<{ + content: { + total: number; + alerts: ParsedTechnicalFields[]; + }; + }> => { + const { + featureIds, + start: startAsDatemath, + end: endAsDatemath, + filter, + } = resources.params.body; + + const racContext = await resources.context.rac; + const alertsClient = await racContext.getAlertsClient(); + + const start = datemath.parse(startAsDatemath)!.valueOf(); + const end = datemath.parse(endAsDatemath)!.valueOf(); + + const kqlQuery = !filter ? [] : [toElasticsearchQuery(fromKueryExpression(filter))]; + + const response = await alertsClient.find({ + featureIds, + + query: { + bool: { + filter: [ + { + range: { + '@timestamp': { + gte: start, + lte: end, + }, + }, + }, + ...kqlQuery, + ], + }, + }, + }); + + // trim some fields + const alerts = response.hits.hits.map((hit) => + omit(hit._source, ...OMITTED_ALERT_FIELDS) + ) as unknown as ParsedTechnicalFields[]; + + return { + content: { + total: (response.hits as { total: { value: number } }).total.value, + alerts, + }, + }; + }, +}); + const functionRecallRoute = createObservabilityAIAssistantServerRoute({ endpoint: 'POST /internal/observability_ai_assistant/functions/recall', params: t.type({ body: t.type({ - query: nonEmptyStringRt, + queries: t.array(nonEmptyStringRt), }), }), options: { tags: ['access:ai_assistant'], }, - handler: async (resources): Promise<{ entries: KnowledgeBaseEntry[] }> => { + handler: async ( + resources + ): Promise<{ entries: Array> }> => { const client = await resources.service.getClient({ request: resources.request }); if (!client) { throw notImplemented(); } - return client.recall(resources.params.body.query); + return client.recall(resources.params.body.queries); }, }); @@ -77,6 +171,7 @@ const functionSummariseRoute = createObservabilityAIAssistantServerRoute({ confidence: t.union([t.literal('low'), t.literal('medium'), t.literal('high')]), is_correction: toBooleanRt, public: toBooleanRt, + labels: t.record(t.string, t.string), }), }), options: { @@ -95,6 +190,7 @@ const functionSummariseRoute = createObservabilityAIAssistantServerRoute({ is_correction: isCorrection, text, public: isPublic, + labels, } = resources.params.body; return client.summarise({ @@ -104,6 +200,7 @@ const functionSummariseRoute = createObservabilityAIAssistantServerRoute({ is_correction: isCorrection, text, public: isPublic, + labels, }, }); }, @@ -159,4 +256,5 @@ export const functionRoutes = { ...functionSummariseRoute, ...setupKnowledgeBaseRoute, ...getKnowledgeBaseStatus, + ...functionAlertsRoute, }; diff --git a/x-pack/plugins/observability_ai_assistant/server/routes/types.ts b/x-pack/plugins/observability_ai_assistant/server/routes/types.ts index e1c256aefcb0e..1766f5c2d5542 100644 --- a/x-pack/plugins/observability_ai_assistant/server/routes/types.ts +++ b/x-pack/plugins/observability_ai_assistant/server/routes/types.ts @@ -5,17 +5,22 @@ * 2.0. */ +import type { CustomRequestHandlerContext, KibanaRequest } from '@kbn/core/server'; import type { Logger } from '@kbn/logging'; -import type { KibanaRequest, RequestHandlerContext } from '@kbn/core/server'; +import type { RacApiRequestHandlerContext } from '@kbn/rule-registry-plugin/server'; +import type { ObservabilityAIAssistantService } from '../service'; import type { ObservabilityAIAssistantPluginSetupDependencies, ObservabilityAIAssistantPluginStartDependencies, } from '../types'; -import type { ObservabilityAIAssistantService } from '../service'; + +export type ObservabilityAIAssistantRequestHandlerContext = CustomRequestHandlerContext<{ + rac: RacApiRequestHandlerContext; +}>; export interface ObservabilityAIAssistantRouteHandlerResources { request: KibanaRequest; - context: RequestHandlerContext; + context: ObservabilityAIAssistantRequestHandlerContext; logger: Logger; service: ObservabilityAIAssistantService; plugins: { diff --git a/x-pack/plugins/observability_ai_assistant/server/service/client/index.ts b/x-pack/plugins/observability_ai_assistant/server/service/client/index.ts index ceb04475e8563..dd256c4784831 100644 --- a/x-pack/plugins/observability_ai_assistant/server/service/client/index.ts +++ b/x-pack/plugins/observability_ai_assistant/server/service/client/index.ts @@ -6,7 +6,7 @@ */ import type { SearchHit } from '@elastic/elasticsearch/lib/api/types'; import { internal, notFound } from '@hapi/boom'; -import type { ActionsClient } from '@kbn/actions-plugin/server/actions_client'; +import type { ActionsClient } from '@kbn/actions-plugin/server'; import type { ElasticsearchClient } from '@kbn/core/server'; import type { Logger } from '@kbn/logging'; import type { PublicMethodsOf } from '@kbn/utility-types'; @@ -108,11 +108,13 @@ export class ObservabilityAIAssistantClient { messages, connectorId, functions, + functionCall, stream = true, }: { messages: Message[]; connectorId: string; functions?: Array<{ name: string; description: string; parameters: CompatibleJSONSchema }>; + functionCall?: string; stream?: TStream; }): Promise => { const messagesForOpenAI: ChatCompletionRequestMessage[] = compact( @@ -140,6 +142,7 @@ export class ObservabilityAIAssistantClient { stream: true, functions: functionsForOpenAI, temperature: 0, + function_call: functionCall ? { name: functionCall } : undefined, }; const executeResult = await this.dependencies.actionsClient.execute({ @@ -312,11 +315,13 @@ export class ObservabilityAIAssistantClient { return createdConversation; }; - recall = async (query: string): Promise<{ entries: KnowledgeBaseEntry[] }> => { + recall = async ( + queries: string[] + ): Promise<{ entries: Array> }> => { return this.dependencies.knowledgeBaseService.recall({ namespace: this.dependencies.namespace, user: this.dependencies.user, - query, + queries, }); }; diff --git a/x-pack/plugins/observability_ai_assistant/server/service/index.ts b/x-pack/plugins/observability_ai_assistant/server/service/index.ts index 2ce0868aefd30..e3a0eb9f15469 100644 --- a/x-pack/plugins/observability_ai_assistant/server/service/index.ts +++ b/x-pack/plugins/observability_ai_assistant/server/service/index.ts @@ -13,13 +13,13 @@ import type { SecurityPluginStart } from '@kbn/security-plugin/server'; import { getSpaceIdFromPath } from '@kbn/spaces-plugin/common'; import type { TaskManagerSetupContract } from '@kbn/task-manager-plugin/server'; import { once } from 'lodash'; -import { KnowledgeBaseEntry } from '../../common/types'; import type { ObservabilityAIAssistantPluginStartDependencies } from '../types'; import { ObservabilityAIAssistantClient } from './client'; import { conversationComponentTemplate } from './conversation_component_template'; import { kbComponentTemplate } from './kb_component_template'; -import { KnowledgeBaseService } from './kb_service'; +import { KnowledgeBaseEntryOperationType, KnowledgeBaseService } from './kb_service'; import type { ObservabilityAIAssistantResourceNames } from './types'; +import { splitKbText } from './util/split_kb_text'; function getResourceName(resource: string) { return `.kibana-observability-ai-assistant-${resource}`; @@ -82,7 +82,7 @@ export class ObservabilityAIAssistantService { return { run: async () => { if (this.kbService) { - // await this.kbService.processQueue(); + await this.kbService.processQueue(); } }, }; @@ -256,20 +256,52 @@ export class ObservabilityAIAssistantService { }); } - async addToKnowledgeBase( + addToKnowledgeBase( entries: Array< - Omit + | { + id: string; + text: string; + } + | { + id: string; + texts: string[]; + } > - ): Promise { - await this.init(); - this.kbService!.store( - entries.map((entry) => ({ - ...entry, - '@timestamp': new Date().toISOString(), - public: true, - confidence: 'high', - is_correction: false, - })) - ); + ): void { + this.init() + .then(() => { + this.kbService!.queue( + entries.flatMap((entry) => { + const entryWithSystemProperties = { + ...entry, + '@timestamp': new Date().toISOString(), + public: true, + confidence: 'high' as const, + is_correction: false, + labels: { + document_id: entry.id, + }, + }; + + const operations = + 'texts' in entryWithSystemProperties + ? splitKbText(entryWithSystemProperties) + : [ + { + type: KnowledgeBaseEntryOperationType.Index, + document: entryWithSystemProperties, + }, + ]; + + return operations; + }) + ); + }) + .catch((error) => { + this.logger.error( + `Could not index ${entries.length} entries because of an initialisation error` + ); + this.logger.error(error); + }); } } diff --git a/x-pack/plugins/observability_ai_assistant/server/service/kb_component_template.ts b/x-pack/plugins/observability_ai_assistant/server/service/kb_component_template.ts index 55d6bbd15519c..8d0e5ff423b2c 100644 --- a/x-pack/plugins/observability_ai_assistant/server/service/kb_component_template.ts +++ b/x-pack/plugins/observability_ai_assistant/server/service/kb_component_template.ts @@ -20,6 +20,11 @@ const date = { type: 'date' as const, }; +const dynamic = { + type: 'object' as const, + dynamic: true, +}; + export const kbComponentTemplate: ClusterComponentTemplate['component_template']['template'] = { mappings: { dynamic: false, @@ -32,6 +37,7 @@ export const kbComponentTemplate: ClusterComponentTemplate['component_template'] name: keyword, }, }, + labels: dynamic, conversation: { properties: { id: keyword, diff --git a/x-pack/plugins/observability_ai_assistant/server/service/kb_service/index.ts b/x-pack/plugins/observability_ai_assistant/server/service/kb_service/index.ts index deed5c41fd480..eab3a0a2fbe57 100644 --- a/x-pack/plugins/observability_ai_assistant/server/service/kb_service/index.ts +++ b/x-pack/plugins/observability_ai_assistant/server/service/kb_service/index.ts @@ -6,12 +6,13 @@ */ import { errors } from '@elastic/elasticsearch'; import type { QueryDslTextExpansionQuery } from '@elastic/elasticsearch/lib/api/types'; -import { serverUnavailable } from '@hapi/boom'; +import { serverUnavailable, gatewayTimeout } from '@hapi/boom'; import type { ElasticsearchClient } from '@kbn/core/server'; import type { Logger } from '@kbn/logging'; -import { TaskManagerStartContract } from '@kbn/task-manager-plugin/server'; +import type { TaskManagerStartContract } from '@kbn/task-manager-plugin/server'; import pLimit from 'p-limit'; import pRetry from 'p-retry'; +import { map } from 'lodash'; import { INDEX_QUEUED_DOCUMENTS_TASK_ID, INDEX_QUEUED_DOCUMENTS_TASK_TYPE } from '..'; import type { KnowledgeBaseEntry } from '../../../common/types'; import type { ObservabilityAIAssistantResourceNames } from '../types'; @@ -24,16 +25,44 @@ interface Dependencies { taskManagerStart: TaskManagerStartContract; } +function isAlreadyExistsError(error: Error) { + return ( + error instanceof errors.ResponseError && + (error.body.error.type === 'resource_not_found_exception' || + error.body.error.type === 'status_exception') + ); +} + const ELSER_MODEL_ID = '.elser_model_1'; function throwKnowledgeBaseNotReady(body: any) { throw serverUnavailable(`Knowledge base is not ready yet`, body); } +export enum KnowledgeBaseEntryOperationType { + Index = 'index', + Delete = 'delete', +} + +interface KnowledgeBaseDeleteOperation { + type: KnowledgeBaseEntryOperationType.Delete; + id?: string; + labels?: Record; +} + +interface KnowledgeBaseIndexOperation { + type: KnowledgeBaseEntryOperationType.Index; + document: KnowledgeBaseEntry; +} + +export type KnowledgeBaseEntryOperation = + | KnowledgeBaseDeleteOperation + | KnowledgeBaseIndexOperation; + export class KnowledgeBaseService { private hasSetup: boolean = false; - private entryQueue: KnowledgeBaseEntry[] = []; + private _queue: KnowledgeBaseEntryOperation[] = []; constructor(private readonly dependencies: Dependencies) { this.ensureTaskScheduled(); @@ -51,93 +80,120 @@ export class KnowledgeBaseService { }, }) .then(() => { - this.dependencies.logger.debug('Scheduled document queue task'); + this.dependencies.logger.debug('Scheduled queue task'); return this.dependencies.taskManagerStart.runSoon(INDEX_QUEUED_DOCUMENTS_TASK_ID); }) .then(() => { - this.dependencies.logger.debug('Document queue task ran'); + this.dependencies.logger.debug('Queue task ran'); }) .catch((err) => { - this.dependencies.logger.error(`Failed to schedule document queue task`); + this.dependencies.logger.error(`Failed to schedule queue task`); this.dependencies.logger.error(err); }); } + private async processOperation(operation: KnowledgeBaseEntryOperation) { + if (operation.type === KnowledgeBaseEntryOperationType.Delete) { + await this.dependencies.esClient.deleteByQuery({ + index: this.dependencies.resources.aliases.kb, + query: { + bool: { + filter: [ + ...(operation.id ? [{ term: { _id: operation.id } }] : []), + ...(operation.labels + ? map(operation.labels, (value, key) => { + return { term: { [key]: value } }; + }) + : []), + ], + }, + }, + }); + return; + } + + await this.summarise({ + entry: operation.document, + }); + } + async processQueue() { - if (!this.entryQueue.length) { + if (!this._queue.length) { return; } if (!(await this.status()).ready) { - this.dependencies.logger.debug(`Bailing on document queue task: KB is not ready yet`); + this.dependencies.logger.debug(`Bailing on queue task: KB is not ready yet`); return; } - this.dependencies.logger.debug(`Processing document queue`); + this.dependencies.logger.debug(`Processing queue`); this.hasSetup = true; - this.dependencies.logger.info(`Indexing ${this.entryQueue.length} queued entries into KB`); + this.dependencies.logger.info(`Processing ${this._queue.length} queue operations`); const limiter = pLimit(5); - const entries = this.entryQueue.concat(); + const operations = this._queue.concat(); await Promise.all( - entries.map((entry) => - limiter(() => { - this.entryQueue.splice(entries.indexOf(entry), 1); - return this.summarise({ entry }); + operations.map((operation) => + limiter(async () => { + this._queue.splice(operations.indexOf(operation), 1); + await this.processOperation(operation); }) ) ); - this.dependencies.logger.info('Indexed all queued entries into KB'); + this.dependencies.logger.info('Processed all queued operations'); } - async store(entries: KnowledgeBaseEntry[]) { - if (!entries.length) { + queue(operations: KnowledgeBaseEntryOperation[]): void { + if (!operations.length) { return; } if (!this.hasSetup) { - this.entryQueue.push(...entries); + this._queue.push(...operations); return; } const limiter = pLimit(5); - const limitedFunctions = entries.map((entry) => limiter(() => this.summarise({ entry }))); + const limitedFunctions = this._queue.map((operation) => + limiter(() => this.processOperation(operation)) + ); Promise.all(limitedFunctions).catch((err) => { - this.dependencies.logger.error(`Failed to index all knowledge base entries`); + this.dependencies.logger.error(`Failed to process all queued operations`); this.dependencies.logger.error(err); }); } recall = async ({ user, - query, + queries, namespace, }: { - query: string; + queries: string[]; user: { name: string }; namespace: string; - }): Promise<{ entries: KnowledgeBaseEntry[] }> => { + }): Promise<{ entries: Array> }> => { try { - const response = await this.dependencies.esClient.search({ + const response = await this.dependencies.esClient.search< + Pick + >({ index: this.dependencies.resources.aliases.kb, query: { bool: { - should: [ - { - text_expansion: { - 'ml.tokens': { - model_text: query, - model_id: '.elser_model_1', - }, - } as unknown as QueryDslTextExpansionQuery, - }, - ], + should: queries.map((query) => ({ + text_expansion: { + 'ml.tokens': { + model_text: query, + model_id: '.elser_model_1', + }, + } as unknown as QueryDslTextExpansionQuery, + })), filter: [ ...getAccessQuery({ user, @@ -146,19 +202,21 @@ export class KnowledgeBaseService { ], }, }, - size: 3, + size: 5, _source: { - includes: ['text', 'id'], + includes: ['text', 'is_correction', 'labels'], }, }); - return { entries: response.hits.hits.map((hit) => ({ ...hit._source!, score: hit._score })) }; + return { + entries: response.hits.hits.map((hit) => ({ + ...hit._source!, + score: hit._score, + id: hit._id, + })), + }; } catch (error) { - if ( - (error instanceof errors.ResponseError && - error.body.error.type === 'resource_not_found_exception') || - error.body.error.type === 'status_exception' - ) { + if (isAlreadyExistsError(error)) { throwKnowledgeBaseNotReady(error.body); } throw error; @@ -185,6 +243,7 @@ export class KnowledgeBaseService { namespace, }, pipeline: this.dependencies.resources.pipelines.kb, + refresh: false, }); } catch (error) { if (error instanceof errors.ResponseError && error.body.error.type === 'status_exception') { @@ -216,7 +275,7 @@ export class KnowledgeBaseService { }; setup = async () => { - // if this fails, it's fine to propagate the error to the user + const retryOptions = { factor: 1, minTimeout: 10000, retries: 12 }; const installModel = async () => { this.dependencies.logger.info('Installing ELSER model'); @@ -234,26 +293,36 @@ export class KnowledgeBaseService { this.dependencies.logger.info('Finished installing ELSER model'); }; - try { + const getIsModelInstalled = async () => { const getResponse = await this.dependencies.esClient.ml.getTrainedModels({ model_id: ELSER_MODEL_ID, include: 'definition_status', }); - if (!getResponse.trained_model_configs[0]?.fully_defined) { - this.dependencies.logger.info('Model is not fully defined'); - await installModel(); + this.dependencies.logger.debug( + 'Model definition status:\n' + JSON.stringify(getResponse.trained_model_configs[0]) + ); + + return Boolean(getResponse.trained_model_configs[0]?.fully_defined); + }; + + await pRetry(async () => { + let isModelInstalled: boolean = false; + try { + isModelInstalled = await getIsModelInstalled(); + } catch (error) { + if (isAlreadyExistsError(error)) { + await installModel(); + isModelInstalled = await getIsModelInstalled(); + } } - } catch (error) { - if ( - error instanceof errors.ResponseError && - error.body.error.type === 'resource_not_found_exception' - ) { - await installModel(); - } else { - throw error; + + if (!isModelInstalled) { + throwKnowledgeBaseNotReady({ + message: 'Model is not fully defined', + }); } - } + }, retryOptions); try { await this.dependencies.esClient.ml.startTrainedModelDeployment({ @@ -261,32 +330,30 @@ export class KnowledgeBaseService { wait_for: 'fully_allocated', }); } catch (error) { - if ( - !(error instanceof errors.ResponseError && error.body.error.type === 'status_exception') - ) { + this.dependencies.logger.debug('Error starting model deployment'); + this.dependencies.logger.debug(error); + if (!isAlreadyExistsError(error)) { throw error; } } - await pRetry( - async () => { - const response = await this.dependencies.esClient.ml.getTrainedModelsStats({ - model_id: ELSER_MODEL_ID, - }); + await pRetry(async () => { + const response = await this.dependencies.esClient.ml.getTrainedModelsStats({ + model_id: ELSER_MODEL_ID, + }); - if ( - response.trained_model_stats[0]?.deployment_stats?.allocation_status.state === - 'fully_allocated' - ) { - return Promise.resolve(); - } + if ( + response.trained_model_stats[0]?.deployment_stats?.allocation_status.state === + 'fully_allocated' + ) { + return Promise.resolve(); + } - this.dependencies.logger.debug('Model is not allocated yet'); + this.dependencies.logger.debug('Model is not allocated yet'); + this.dependencies.logger.debug(JSON.stringify(response)); - return Promise.reject(new Error('Not Ready')); - }, - { factor: 1, minTimeout: 10000, maxRetryTime: 20 * 60 * 1000 } - ); + throw gatewayTimeout(); + }, retryOptions); this.dependencies.logger.info('Model is ready'); this.ensureTaskScheduled(); diff --git a/x-pack/plugins/observability_ai_assistant/server/service/kb_service/kb_docs/lens.ts b/x-pack/plugins/observability_ai_assistant/server/service/kb_service/kb_docs/lens.ts index aa0c6e3ffb937..e4fdc8969c010 100644 --- a/x-pack/plugins/observability_ai_assistant/server/service/kb_service/kb_docs/lens.ts +++ b/x-pack/plugins/observability_ai_assistant/server/service/kb_service/kb_docs/lens.ts @@ -12,9 +12,8 @@ export function addLensDocsToKb(service: ObservabilityAIAssistantService) { service.addToKnowledgeBase([ { id: 'lens_formulas_how_it_works', - text: dedent(`## How it works - - Lens formulas let you do math using a combination of Elasticsearch aggregations and + texts: [ + `Lens formulas let you do math using a combination of Elasticsearch aggregations and math functions. There are three main types of functions: * Elasticsearch metrics, like \`sum(bytes)\` @@ -30,8 +29,8 @@ export function addLensDocsToKb(service: ObservabilityAIAssistantService) { kql='datacenter.name: east*' )) \`\`\` - - Elasticsearch functions take a field name, which can be in quotes. \`sum(bytes)\` is the same + `, + `Elasticsearch functions take a field name, which can be in quotes. \`sum(bytes)\` is the same as \`sum('bytes')\`. Some functions take named arguments, like \`moving_average(count(), window=5)\`. @@ -42,25 +41,23 @@ export function addLensDocsToKb(service: ObservabilityAIAssistantService) { Math functions can take positional arguments, like pow(count(), 3) is the same as count() * count() * count() - Use the symbols +, -, /, and * to perform basic math.`), + Use the symbols +, -, /, and * to perform basic math.`, + ], }, { id: 'lens_common_formulas', - text: dedent(`## Common formulas - - The most common formulas are dividing two values to produce a percent. To display accurately, set - "value format" to "percent". - - ### Filter ratio: + texts: [ + `The most common formulas are dividing two values to produce a percent. To display accurately, set + "value format" to "percent"`, + `### Filter ratio: Use \`kql=''\` to filter one set of documents and compare it to other documents within the same grouping. For example, to see how the error rate changes over time: \`\`\` count(kql='response.status_code > 400') / count() - \`\`\` - - ### Week over week: + \`\`\``, + `### Week over week: Use \`shift='1w'\` to get the value of each grouping from the previous week. Time shift should not be used with the *Top values* function. @@ -68,18 +65,18 @@ export function addLensDocsToKb(service: ObservabilityAIAssistantService) { \`\`\` percentile(system.network.in.bytes, percentile=99) / percentile(system.network.in.bytes, percentile=99, shift='1w') - \`\`\` + \`\`\``, - ### Percent of total + `### Percent of total Formulas can calculate \`overall_sum\` for all the groupings, which lets you convert each grouping into a percent of total: \`\`\` sum(products.base_price) / overall_sum(sum(products.base_price)) - \`\`\` + \`\`\``, - ### Recent change + `### Recent change Use \`reducedTimeRange='30m'\` to add an additional filter on the time range of a metric aligned with the end of the global time range. @@ -88,27 +85,28 @@ export function addLensDocsToKb(service: ObservabilityAIAssistantService) { \`\`\` max(system.network.in.bytes, reducedTimeRange="30m") - min(system.network.in.bytes, reducedTimeRange="30m") - \`\`\` - - `), + \`\`\` + `, + ], }, { id: 'lens_formulas_elasticsearch_functions', - text: dedent(`## Elasticsearch functions + texts: [ + `## Elasticsearch functions These functions will be executed on the raw documents for each row of the resulting table, aggregating all documents matching the break down - dimensions into a single value. + dimensions into a single value.`, - #### average(field: string) + `#### average(field: string) Returns the average of a field. This function only works for number fields. Example: Get the average of price: \`average(price)\` Example: Get the average of price for orders from the UK: \`average(price, - kql='location:UK')\` + kql='location:UK')\``, - #### count([field: string]) + `#### count([field: string]) The total number of documents. When you provide a field, the total number of field values is counted. When you use the Count function for fields that have multiple values in a single document, all values are counted. @@ -118,57 +116,57 @@ export function addLensDocsToKb(service: ObservabilityAIAssistantService) { To calculate the number of products in all orders, use \`count(products.id)\`. To calculate the number of documents that match a specific filter, use - \`count(kql='price > 500')\`. + \`count(kql='price > 500')\`.`, - #### last_value(field: string) + `#### last_value(field: string) Returns the value of a field from the last document, ordered by the default time field of the data view. This function is usefull the retrieve the latest state of an entity. Example: Get the current status of server A: \`last_value(server.status, - kql='server.name="A"')\` + kql='server.name="A"')\``, - #### max(field: string) + `#### max(field: string) Returns the max of a field. This function only works for number fields. Example: Get the max of price: \`max(price)\` Example: Get the max of price for orders from the UK: \`max(price, - kql='location:UK')\` + kql='location:UK')\``, - #### median(field: string) + `#### median(field: string) Returns the median of a field. This function only works for number fields. Example: Get the median of price: \`median(price)\` Example: Get the median of price for orders from the UK: \`median(price, - kql='location:UK')\` + kql='location:UK')\``, - #### min(field: string) + `#### min(field: string) Returns the min of a field. This function only works for number fields. Example: Get the min of price: \`min(price)\` Example: Get the min of price for orders from the UK: \`min(price, - kql='location:UK')\` + kql='location:UK')\``, - #### percentile(field: string, [percentile]: number) + `#### percentile(field: string, [percentile]: number) Returns the specified percentile of the values of a field. This is the value n percent of the values occuring in documents are smaller. Example: Get the number of bytes larger than 95 % of values: - \`percentile(bytes, percentile=95)\` + \`percentile(bytes, percentile=95)\``, - #### percentile_rank(field: string, [value]: number) + `#### percentile_rank(field: string, [value]: number) Returns the percentage of values which are below a certain value. For example, if a value is greater than or equal to 95% of the observed values it is said to be at the 95th percentile rank Example: Get the percentage of values which are below of 100: - \`percentile_rank(bytes, value=100)\` + \`percentile_rank(bytes, value=100)\``, - #### standard_deviation(field: string) + `#### standard_deviation(field: string) Returns the amount of variation or dispersion of the field. The function works only for number fields. @@ -176,17 +174,17 @@ export function addLensDocsToKb(service: ObservabilityAIAssistantService) { \`standard_deviation(price).\` Example: To get the variance of price for orders from the UK, use - \`square(standard_deviation(price, kql='location:UK'))\`. + \`square(standard_deviation(price, kql='location:UK'))\`.`, - #### sum(field: string) + `#### sum(field: string) Returns the sum of a field. This function only works for number fields. Example: Get the sum of price: sum(price) Example: Get the sum of price for orders from the UK: \`sum(price, - kql='location:UK')\` + kql='location:UK')\``, - #### unique_count(field: string) + `#### unique_count(field: string) Calculates the number of unique values of a specified field. Works for number, string, date and boolean values. @@ -196,15 +194,17 @@ export function addLensDocsToKb(service: ObservabilityAIAssistantService) { Example: Calculate the number of different products from the "clothes" group: \`unique_count(product.name, kql='product.group=clothes')\` - `), + `, + ], }, { id: 'lens_formulas_column_functions', - text: dedent(`## Column calculations + texts: [ + `## Column calculations These functions are executed for each row, but are provided with the whole - column as context. This is also known as a window function. - - #### counter_rate(metric: number) + column as context. This is also known as a window function.`, + + `#### counter_rate(metric: number) Calculates the rate of an ever increasing counter. This function will only yield helpful results on counter metric fields which contain a measurement of some kind monotonically growing over time. If the value does get smaller, it @@ -215,9 +215,9 @@ export function addLensDocsToKb(service: ObservabilityAIAssistantService) { or top values dimensions. It uses the current interval when used in Formula. Example: Visualize the rate of bytes received over time by a memcached server: - counter_rate(max(memcached.stats.read.bytes)) - - cumulative_sum(metric: number) + counter_rate(max(memcached.stats.read.bytes))`, + + `cumulative_sum(metric: number) Calculates the cumulative sum of a metric over time, adding all previous values of a series to each value. To use this function, you need to configure a date histogram dimension as well. @@ -226,9 +226,9 @@ export function addLensDocsToKb(service: ObservabilityAIAssistantService) { or top values dimensions. Example: Visualize the received bytes accumulated over time: - cumulative_sum(sum(bytes)) - - differences(metric: number) + cumulative_sum(sum(bytes))`, + + `differences(metric: number) Calculates the difference to the last value of a metric over time. To use this function, you need to configure a date histogram dimension as well. Differences requires the data to be sequential. If your data is empty when using @@ -238,9 +238,9 @@ export function addLensDocsToKb(service: ObservabilityAIAssistantService) { or top values dimensions. Example: Visualize the change in bytes received over time: - differences(sum(bytes)) - - moving_average(metric: number, [window]: number) + differences(sum(bytes))`, + + `moving_average(metric: number, [window]: number) Calculates the moving average of a metric over time, averaging the last n-th values to calculate the current value. To use this function, you need to configure a date histogram dimension as well. The default window value is 5. @@ -251,9 +251,9 @@ export function addLensDocsToKb(service: ObservabilityAIAssistantService) { Takes a named parameter window which specifies how many last values to include in the average calculation for the current value. - Example: Smooth a line of measurements: moving_average(sum(bytes), window=5) - - normalize_by_unit(metric: number, unit: s|m|h|d|w|M|y) + Example: Smooth a line of measurements: moving_average(sum(bytes), window=5)`, + + `normalize_by_unit(metric: number, unit: s|m|h|d|w|M|y) This advanced function is useful for normalizing counts and sums to a specific time interval. It allows for integration with metrics that are stored already normalized to a specific time interval. @@ -264,9 +264,9 @@ export function addLensDocsToKb(service: ObservabilityAIAssistantService) { Example: A ratio comparing an already normalized metric to another metric that needs to be normalized. normalize_by_unit(counter_rate(max(system.diskio.write.bytes)), unit='s') / - last_value(apache.status.bytes_per_second) - - overall_average(metric: number) + last_value(apache.status.bytes_per_second)`, + + `overall_average(metric: number) Calculates the average of a metric for all data points of a series in the current chart. A series is defined by a dimension using a date histogram or interval function. Other dimensions breaking down the data like top values or @@ -276,9 +276,9 @@ export function addLensDocsToKb(service: ObservabilityAIAssistantService) { overall_average is calculating the average over all dimensions no matter the used function - Example: Divergence from the mean: sum(bytes) - overall_average(sum(bytes)) - - overall_max(metric: number) + Example: Divergence from the mean: sum(bytes) - overall_average(sum(bytes))`, + + `overall_max(metric: number) Calculates the maximum of a metric for all data points of a series in the current chart. A series is defined by a dimension using a date histogram or interval function. Other dimensions breaking down the data like top values or @@ -289,9 +289,9 @@ export function addLensDocsToKb(service: ObservabilityAIAssistantService) { function Example: Percentage of range (sum(bytes) - overall_min(sum(bytes))) / - (overall_max(sum(bytes)) - overall_min(sum(bytes))) - - overall_min(metric: number) + (overall_max(sum(bytes)) - overall_min(sum(bytes)))`, + + `overall_min(metric: number) Calculates the minimum of a metric for all data points of a series in the current chart. A series is defined by a dimension using a date histogram or interval function. Other dimensions breaking down the data like top values or @@ -302,9 +302,9 @@ export function addLensDocsToKb(service: ObservabilityAIAssistantService) { function Example: Percentage of range (sum(bytes) - overall_min(sum(bytes)) / - (overall_max(sum(bytes)) - overall_min(sum(bytes))) - - overall_sum(metric: number) + (overall_max(sum(bytes)) - overall_min(sum(bytes)))`, + + `overall_sum(metric: number) Calculates the sum of a metric of all data points of a series in the current chart. A series is defined by a dimension using a date histogram or interval function. Other dimensions breaking down the data like top values or filter are @@ -314,19 +314,21 @@ export function addLensDocsToKb(service: ObservabilityAIAssistantService) { overall_sum is calculating the sum over all dimensions no matter the used function. - Example: Percentage of total sum(bytes) / overall_sum(sum(bytes))`), + Example: Percentage of total sum(bytes) / overall_sum(sum(bytes))`, + ], }, { id: 'lens_formulas_math_functions', - text: dedent(`Math - These functions will be executed for reach row of the resulting table using single values from the same row calculated using other functions. - - abs([value]: number) + texts: [ + `Math + These functions will be executed for reach row of the resulting table using single values from the same row calculated using other functions.`, + + `abs([value]: number) Calculates absolute value. A negative value is multiplied by -1, a positive value stays the same. - Example: Calculate average distance to sea level abs(average(altitude)) - - add([left]: number, [right]: number) + Example: Calculate average distance to sea level abs(average(altitude))`, + + `add([left]: number, [right]: number) Adds up two numbers. Also works with + symbol. @@ -337,9 +339,9 @@ export function addLensDocsToKb(service: ObservabilityAIAssistantService) { Example: Offset count by a static value - add(count(), 5) - - cbrt([value]: number) + add(count(), 5)`, + + `cbrt([value]: number) Cube root of value. Example: Calculate side length from volume @@ -351,9 +353,9 @@ export function addLensDocsToKb(service: ObservabilityAIAssistantService) { Example: Round up price to the next dollar - ceil(sum(price)) - - clamp([value]: number, [min]: number, [max]: number) + ceil(sum(price))`, + + `clamp([value]: number, [min]: number, [max]: number) Limits the value from a minimum to maximum. Example: Make sure to catch outliers @@ -362,22 +364,22 @@ export function addLensDocsToKb(service: ObservabilityAIAssistantService) { average(bytes), percentile(bytes, percentile=5), percentile(bytes, percentile=95) - ) - cube([value]: number) + )`, + `cube([value]: number) Calculates the cube of a number. Example: Calculate volume from side length - cube(last_value(length)) - - defaults([value]: number, [default]: number) + cube(last_value(length))`, + + `defaults([value]: number, [default]: number) Returns a default numeric value when value is null. Example: Return -1 when a field has no data - defaults(average(bytes), -1) - - divide([left]: number, [right]: number) + defaults(average(bytes), -1)`, + + `divide([left]: number, [right]: number) Divides the first number by the second number. Also works with / symbol @@ -386,44 +388,44 @@ export function addLensDocsToKb(service: ObservabilityAIAssistantService) { sum(profit) / sum(revenue) - Example: divide(sum(bytes), 2) - - exp([value]: number) + Example: divide(sum(bytes), 2)`, + + `exp([value]: number) Raises e to the nth power. Example: Calculate the natural exponential function - exp(last_value(duration)) - - fix([value]: number) + exp(last_value(duration))`, + + `fix([value]: number) For positive values, takes the floor. For negative values, takes the ceiling. Example: Rounding towards zero - fix(sum(profit)) - - floor([value]: number) + fix(sum(profit))`, + + `floor([value]: number) Round down to nearest integer value Example: Round down a price - floor(sum(price)) - - log([value]: number, [base]?: number) + floor(sum(price))`, + + `log([value]: number, [base]?: number) Logarithm with optional base. The natural base e is used as default. Example: Calculate number of bits required to store values log(sum(bytes)) - log(sum(bytes), 2) - mod([value]: number, [base]: number) + log(sum(bytes), 2)`, + `mod([value]: number, [base]: number) Remainder after dividing the function by a number Example: Calculate last three digits of a value - mod(sum(price), 1000) - - multiply([left]: number, [right]: number) + mod(sum(price), 1000)`, + + `multiply([left]: number, [right]: number) Multiplies two numbers. Also works with * symbol. @@ -434,63 +436,67 @@ export function addLensDocsToKb(service: ObservabilityAIAssistantService) { Example: Calculate price after constant tax rate - multiply(sum(price), 1.2) - - pick_max([left]: number, [right]: number) + multiply(sum(price), 1.2)`, + + `pick_max([left]: number, [right]: number) Finds the maximum value between two numbers. Example: Find the maximum between two fields averages - pick_max(average(bytes), average(memory)) - - pick_min([left]: number, [right]: number) + pick_max(average(bytes), average(memory))`, + + `pick_min([left]: number, [right]: number) Finds the minimum value between two numbers. Example: Find the minimum between two fields averages - pick_min(average(bytes), average(memory)) - - pow([value]: number, [base]: number) + pick_min(average(bytes), average(memory))`, + + `pow([value]: number, [base]: number) Raises the value to a certain power. The second argument is required Example: Calculate volume based on side length - pow(last_value(length), 3) - - round([value]: number, [decimals]?: number) + pow(last_value(length), 3)`, + + `round([value]: number, [decimals]?: number) Rounds to a specific number of decimal places, default of 0 Examples: Round to the cent round(sum(bytes)) - round(sum(bytes), 2) - sqrt([value]: number) + round(sum(bytes), 2)`, + `sqrt([value]: number) Square root of a positive value only Example: Calculate side length based on area - sqrt(last_value(area)) - - square([value]: number) + sqrt(last_value(area))`, + + `square([value]: number) Raise the value to the 2nd power Example: Calculate area based on side length - square(last_value(length)) - - subtract([left]: number, [right]: number) + square(last_value(length))`, + + `subtract([left]: number, [right]: number) Subtracts the first number from the second number. Also works with - symbol. Example: Calculate the range of a field - subtract(max(bytes), min(bytes)) - - Comparison - These functions are used to perform value comparison. - - eq([left]: number, [right]: number) + subtract(max(bytes), min(bytes))`, + ], + }, + { + id: 'lens_formulas_comparison_functions', + texts: [ + `Comparison + These functions are used to perform value comparison.`, + + `eq([left]: number, [right]: number) Performs an equality comparison between two values. To be used as condition for ifelse comparison function. @@ -501,9 +507,9 @@ export function addLensDocsToKb(service: ObservabilityAIAssistantService) { average(bytes) == average(memory) - Example: eq(sum(bytes), 1000000) - - gt([left]: number, [right]: number) + Example: eq(sum(bytes), 1000000)`, + + `gt([left]: number, [right]: number) Performs a greater than comparison between two values. To be used as condition for ifelse comparison function. @@ -514,9 +520,9 @@ export function addLensDocsToKb(service: ObservabilityAIAssistantService) { average(bytes) > average(memory) - Example: gt(average(bytes), 1000) - - gte([left]: number, [right]: number) + Example: gt(average(bytes), 1000)`, + + `gte([left]: number, [right]: number) Performs a greater than comparison between two values. To be used as condition for ifelse comparison function. @@ -527,16 +533,16 @@ export function addLensDocsToKb(service: ObservabilityAIAssistantService) { average(bytes) >= average(memory) - Example: gte(average(bytes), 1000) - - ifelse([condition]: boolean, [left]: number, [right]: number) + Example: gte(average(bytes), 1000)`, + + `ifelse([condition]: boolean, [left]: number, [right]: number) Returns a value depending on whether the element of condition is true or false. Example: Average revenue per customer but in some cases customer id is not provided which counts as additional customer - sum(total)/(unique_count(customer_id) + ifelse( count() > count(kql='customer_id:*'), 1, 0)) - - lt([left]: number, [right]: number) + sum(total)/(unique_count(customer_id) + ifelse( count() > count(kql='customer_id:*'), 1, 0))`, + + `lt([left]: number, [right]: number) Performs a lower than comparison between two values. To be used as condition for ifelse comparison function. @@ -547,9 +553,9 @@ export function addLensDocsToKb(service: ObservabilityAIAssistantService) { average(bytes) <= average(memory) - Example: lt(average(bytes), 1000) - - lte([left]: number, [right]: number) + Example: lt(average(bytes), 1000)`, + + `lte([left]: number, [right]: number) Performs a lower than or equal comparison between two values. To be used as condition for ifelse comparison function. @@ -560,7 +566,8 @@ export function addLensDocsToKb(service: ObservabilityAIAssistantService) { average(bytes) <= average(memory) - Example: lte(average(bytes), 1000)`), + Example: lte(average(bytes), 1000)`, + ], }, { id: 'lens_formulas_kibana_context', diff --git a/x-pack/plugins/observability_ai_assistant/server/service/util/split_kb_text.ts b/x-pack/plugins/observability_ai_assistant/server/service/util/split_kb_text.ts new file mode 100644 index 0000000000000..e2b5b7c2c5784 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/server/service/util/split_kb_text.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { merge } from 'lodash'; +import type { KnowledgeBaseEntry } from '../../../common/types'; +import { type KnowledgeBaseEntryOperation, KnowledgeBaseEntryOperationType } from '../kb_service'; + +export function splitKbText({ + id, + texts, + ...rest +}: Omit & { texts: string[] }): KnowledgeBaseEntryOperation[] { + return [ + { + type: KnowledgeBaseEntryOperationType.Delete, + labels: { + document_id: id, + }, + }, + ...texts.map((text, index) => ({ + type: KnowledgeBaseEntryOperationType.Index, + document: merge({}, rest, { + id: [id, index].join('_'), + labels: { + document_id: id, + }, + text, + }), + })), + ]; +} diff --git a/x-pack/plugins/observability_ai_assistant/tsconfig.json b/x-pack/plugins/observability_ai_assistant/tsconfig.json index 8c49398e2deab..ddbd38c21bc5d 100644 --- a/x-pack/plugins/observability_ai_assistant/tsconfig.json +++ b/x-pack/plugins/observability_ai_assistant/tsconfig.json @@ -42,7 +42,9 @@ "@kbn/field-formats-plugin", "@kbn/lens-plugin", "@kbn/data-views-plugin", - "@kbn/task-manager-plugin" + "@kbn/task-manager-plugin", + "@kbn/es-query", + "@kbn/rule-registry-plugin" ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/profiling/common/setup.test.ts b/x-pack/plugins/profiling/common/setup.test.ts index 100c0efa5cace..48b8136e39020 100644 --- a/x-pack/plugins/profiling/common/setup.test.ts +++ b/x-pack/plugins/profiling/common/setup.test.ts @@ -6,8 +6,7 @@ */ import { - areResourcesSetupForAdmin, - areResourcesSetupForViewer, + areResourcesSetup, createDefaultSetupState, mergePartialSetupStates, PartialSetupState, @@ -54,178 +53,129 @@ function createSettingsState(configured: boolean): PartialSetupState { } describe('Merging partial state operations', () => { - describe('Merge states', () => { - const defaultSetupState = createDefaultSetupState(); - - it('returns partial states with missing key', () => { - const mergedState = mergePartialSetupStates(defaultSetupState, [ - createCloudState(true), - createDataState(true), - ]); - - expect(mergedState.cloud.available).toEqual(true); - expect(mergedState.cloud.required).toEqual(true); - expect(mergedState.data.available).toEqual(true); - }); - - it('should deeply nested partial states with overlap', () => { - const mergedState = mergePartialSetupStates(defaultSetupState, [ - createCollectorPolicyState(true), - createSymbolizerPolicyState(true), - ]); - - expect(mergedState.policies.collector.installed).toEqual(true); - expect(mergedState.policies.symbolizer.installed).toEqual(true); - }); + const defaultSetupState = createDefaultSetupState(); + + it('returns partial states with missing key', () => { + const mergedState = mergePartialSetupStates(defaultSetupState, [ + createCloudState(true), + createDataState(true), + ]); + + expect(mergedState.cloud.available).toEqual(true); + expect(mergedState.cloud.required).toEqual(true); + expect(mergedState.data.available).toEqual(true); + }); + + it('should deeply nested partial states with overlap', () => { + const mergedState = mergePartialSetupStates(defaultSetupState, [ + createCollectorPolicyState(true), + createSymbolizerPolicyState(true), + ]); + + expect(mergedState.policies.collector.installed).toEqual(true); + expect(mergedState.policies.symbolizer.installed).toEqual(true); }); - describe('For admin users', () => { - const defaultSetupState = createDefaultSetupState(); - it('returns false when permission is not configured', () => { - const mergedState = mergePartialSetupStates(defaultSetupState, [ - createCollectorPolicyState(true), - createSymbolizerPolicyState(true), - createProfilingInApmPolicyState(true), - createResourceState({ enabled: true, created: true }), - createSettingsState(true), - createPermissionState(false), - ]); - - expect(areResourcesSetupForAdmin(mergedState)).toBeFalsy(); - }); - - it('returns false when resource management is not enabled', () => { - const mergedState = mergePartialSetupStates(defaultSetupState, [ - createCollectorPolicyState(true), - createSymbolizerPolicyState(true), - createProfilingInApmPolicyState(true), - createResourceState({ enabled: false, created: true }), - createSettingsState(true), - createPermissionState(true), - ]); - - expect(areResourcesSetupForAdmin(mergedState)).toBeFalsy(); - }); - - it('returns false when resources are not created', () => { - const mergedState = mergePartialSetupStates(defaultSetupState, [ - createCollectorPolicyState(true), - createSymbolizerPolicyState(true), - createProfilingInApmPolicyState(true), - createResourceState({ enabled: true, created: false }), - createSettingsState(true), - createPermissionState(true), - ]); - - expect(areResourcesSetupForAdmin(mergedState)).toBeFalsy(); - }); - - it('returns false when settings are not configured', () => { - const mergedState = mergePartialSetupStates(defaultSetupState, [ - createCollectorPolicyState(true), - createSymbolizerPolicyState(true), - createProfilingInApmPolicyState(true), - createResourceState({ enabled: true, created: true }), - createSettingsState(false), - createPermissionState(true), - ]); - - expect(areResourcesSetupForAdmin(mergedState)).toBeFalsy(); - }); - - it('returns true when all checks are valid', () => { - const mergedState = mergePartialSetupStates(defaultSetupState, [ - createCollectorPolicyState(true), - createSymbolizerPolicyState(true), - createProfilingInApmPolicyState(false), - createResourceState({ enabled: true, created: true }), - createSettingsState(true), - createPermissionState(true), - ]); - - expect(areResourcesSetupForAdmin(mergedState)).toBeTruthy(); - }); - - it('returns false when collector is not found', () => { - const mergedState = mergePartialSetupStates(defaultSetupState, [ - createCollectorPolicyState(false), - createSymbolizerPolicyState(true), - createProfilingInApmPolicyState(false), - createResourceState({ enabled: true, created: true }), - createSettingsState(true), - createPermissionState(true), - ]); - - expect(areResourcesSetupForAdmin(mergedState)).toBeFalsy(); - }); - - it('returns false when symbolizer is not found', () => { - const mergedState = mergePartialSetupStates(defaultSetupState, [ - createCollectorPolicyState(true), - createSymbolizerPolicyState(false), - createProfilingInApmPolicyState(false), - createResourceState({ enabled: true, created: true }), - createSettingsState(true), - createPermissionState(true), - ]); - - expect(areResourcesSetupForAdmin(mergedState)).toBeFalsy(); - }); - - it('returns false when profiling is in APM server', () => { - const mergedState = mergePartialSetupStates(defaultSetupState, [ - createCollectorPolicyState(true), - createSymbolizerPolicyState(true), - createProfilingInApmPolicyState(true), - createResourceState({ enabled: true, created: true }), - createSettingsState(true), - createPermissionState(true), - ]); - - expect(areResourcesSetupForAdmin(mergedState)).toBeFalsy(); - }); + it('returns false when permission is not configured', () => { + const mergedState = mergePartialSetupStates(defaultSetupState, [ + createCollectorPolicyState(true), + createSymbolizerPolicyState(true), + createProfilingInApmPolicyState(true), + createResourceState({ enabled: true, created: true }), + createSettingsState(true), + createPermissionState(false), + ]); + + expect(areResourcesSetup(mergedState)).toBeFalsy(); + }); + + it('returns false when resource management is not enabled', () => { + const mergedState = mergePartialSetupStates(defaultSetupState, [ + createCollectorPolicyState(true), + createSymbolizerPolicyState(true), + createProfilingInApmPolicyState(true), + createResourceState({ enabled: false, created: true }), + createSettingsState(true), + createPermissionState(true), + ]); + + expect(areResourcesSetup(mergedState)).toBeFalsy(); + }); + + it('returns false when resources are not created', () => { + const mergedState = mergePartialSetupStates(defaultSetupState, [ + createCollectorPolicyState(true), + createSymbolizerPolicyState(true), + createProfilingInApmPolicyState(true), + createResourceState({ enabled: true, created: false }), + createSettingsState(true), + createPermissionState(true), + ]); + + expect(areResourcesSetup(mergedState)).toBeFalsy(); + }); + + it('returns false when settings are not configured', () => { + const mergedState = mergePartialSetupStates(defaultSetupState, [ + createCollectorPolicyState(true), + createSymbolizerPolicyState(true), + createProfilingInApmPolicyState(true), + createResourceState({ enabled: true, created: true }), + createSettingsState(false), + createPermissionState(true), + ]); + + expect(areResourcesSetup(mergedState)).toBeFalsy(); + }); + + it('returns true when all checks are valid', () => { + const mergedState = mergePartialSetupStates(defaultSetupState, [ + createCollectorPolicyState(true), + createSymbolizerPolicyState(true), + createProfilingInApmPolicyState(false), + createResourceState({ enabled: true, created: true }), + createSettingsState(true), + createPermissionState(true), + ]); + + expect(areResourcesSetup(mergedState)).toBeTruthy(); + }); + + it('returns false when collector is not found', () => { + const mergedState = mergePartialSetupStates(defaultSetupState, [ + createCollectorPolicyState(false), + createSymbolizerPolicyState(true), + createProfilingInApmPolicyState(false), + createResourceState({ enabled: true, created: true }), + createSettingsState(true), + createPermissionState(true), + ]); + + expect(areResourcesSetup(mergedState)).toBeFalsy(); + }); + + it('returns false when symbolizer is not found', () => { + const mergedState = mergePartialSetupStates(defaultSetupState, [ + createCollectorPolicyState(true), + createSymbolizerPolicyState(false), + createProfilingInApmPolicyState(false), + createResourceState({ enabled: true, created: true }), + createSettingsState(true), + createPermissionState(true), + ]); + + expect(areResourcesSetup(mergedState)).toBeFalsy(); }); - describe('For viewer users', () => { - const defaultSetupState = createDefaultSetupState(); - - it('returns false when collector is not installed', () => { - const mergedState = mergePartialSetupStates(defaultSetupState, [ - createCollectorPolicyState(false), - createSymbolizerPolicyState(true), - createProfilingInApmPolicyState(false), - ]); - - expect(areResourcesSetupForViewer(mergedState)).toBeFalsy(); - }); - - it('returns false when symbolizer is not installed', () => { - const mergedState = mergePartialSetupStates(defaultSetupState, [ - createCollectorPolicyState(true), - createSymbolizerPolicyState(false), - createProfilingInApmPolicyState(false), - ]); - - expect(areResourcesSetupForViewer(mergedState)).toBeFalsy(); - }); - - it('returns false when profiling is configured in APM policy', () => { - const mergedState = mergePartialSetupStates(defaultSetupState, [ - createCollectorPolicyState(true), - createSymbolizerPolicyState(true), - createProfilingInApmPolicyState(true), - ]); - - expect(areResourcesSetupForViewer(mergedState)).toBeFalsy(); - }); - - it('returns true when all checks are valid', () => { - const mergedState = mergePartialSetupStates(defaultSetupState, [ - createCollectorPolicyState(true), - createSymbolizerPolicyState(true), - createProfilingInApmPolicyState(false), - ]); - - expect(areResourcesSetupForViewer(mergedState)).toBeTruthy(); - }); + it('returns false when profiling is in APM server', () => { + const mergedState = mergePartialSetupStates(defaultSetupState, [ + createCollectorPolicyState(true), + createSymbolizerPolicyState(true), + createProfilingInApmPolicyState(true), + createResourceState({ enabled: true, created: true }), + createSettingsState(true), + createPermissionState(true), + ]); + + expect(areResourcesSetup(mergedState)).toBeFalsy(); }); }); diff --git a/x-pack/plugins/profiling/common/setup.ts b/x-pack/plugins/profiling/common/setup.ts index e1ee768d9d3d0..facae98f25012 100644 --- a/x-pack/plugins/profiling/common/setup.ts +++ b/x-pack/plugins/profiling/common/setup.ts @@ -35,6 +35,7 @@ export interface SetupState { }; resources: { created: boolean; + pre_8_9_1_data: boolean; }; settings: { configured: boolean; @@ -71,6 +72,7 @@ export function createDefaultSetupState(): SetupState { }, resources: { created: false, + pre_8_9_1_data: false, }, settings: { configured: false, @@ -78,17 +80,11 @@ export function createDefaultSetupState(): SetupState { }; } -export function areResourcesSetupForViewer(state: SetupState): boolean { +export function areResourcesSetup(state: SetupState): boolean { return ( state.policies.collector.installed && state.policies.symbolizer.installed && - !state.policies.apm.profilingEnabled - ); -} - -export function areResourcesSetupForAdmin(state: SetupState): boolean { - return ( - areResourcesSetupForViewer(state) && + !state.policies.apm.profilingEnabled && state.resource_management.enabled && state.resources.created && state.permissions.configured && diff --git a/x-pack/plugins/profiling/common/stack_traces.ts b/x-pack/plugins/profiling/common/stack_traces.ts index 8266f32a19f1e..97a18d09ed389 100644 --- a/x-pack/plugins/profiling/common/stack_traces.ts +++ b/x-pack/plugins/profiling/common/stack_traces.ts @@ -24,6 +24,7 @@ export interface ProfilingStatusResponse { }; resources: { created: boolean; + pre_8_9_1_data: boolean; }; } diff --git a/x-pack/plugins/profiling/e2e/cypress/e2e/empty_state/home.cy.ts b/x-pack/plugins/profiling/e2e/cypress/e2e/empty_state/home.cy.ts index 26f2347a62340..bba7a3c014c41 100644 --- a/x-pack/plugins/profiling/e2e/cypress/e2e/empty_state/home.cy.ts +++ b/x-pack/plugins/profiling/e2e/cypress/e2e/empty_state/home.cy.ts @@ -10,9 +10,13 @@ describe('Home page with empty state', () => { cy.loginAsElastic(); }); - it('shows the empty state when Profiling has not been set up', () => { + it('shows Set up page when Profiling has not been set up', () => { cy.intercept('GET', '/internal/profiling/setup/es_resources', { - fixture: 'es_resources_setup_false.json', + body: { + has_setup: false, + has_data: false, + pre_8_9_1_data: false, + }, }).as('getEsResources'); cy.visitKibana('/app/profiling'); cy.wait('@getEsResources'); @@ -20,9 +24,13 @@ describe('Home page with empty state', () => { cy.contains('Set up Universal Profiling'); }); - it('shows the tutorial after Profiling has been set up', () => { + it('shows Add data page after Profiling has been set up', () => { cy.intercept('GET', '/internal/profiling/setup/es_resources', { - fixture: 'es_resources_data_false.json', + body: { + has_setup: true, + has_data: false, + pre_8_9_1_data: false, + }, }).as('getEsResources'); cy.visitKibana('/app/profiling'); cy.wait('@getEsResources'); @@ -34,4 +42,45 @@ describe('Home page with empty state', () => { cy.contains('RPM Package'); cy.contains('Upload Symbols'); }); + + describe('Delete Data View', () => { + it('shows Delete page when setup is false', () => { + cy.intercept('GET', '/internal/profiling/setup/es_resources', { + body: { + has_setup: false, + has_data: true, + pre_8_9_1_data: true, + }, + }).as('getEsResources'); + cy.visitKibana('/app/profiling'); + cy.wait('@getEsResources'); + cy.contains('Delete existing profiling data'); + }); + + it('shows Delete page when data pre 8.9.1 is still available and data is found', () => { + cy.intercept('GET', '/internal/profiling/setup/es_resources', { + body: { + has_setup: true, + has_data: true, + pre_8_9_1_data: true, + }, + }).as('getEsResources'); + cy.visitKibana('/app/profiling'); + cy.wait('@getEsResources'); + cy.contains('Delete existing profiling data'); + }); + + it('shows Delete page when data pre 8.9.1 is still available and data is not found', () => { + cy.intercept('GET', '/internal/profiling/setup/es_resources', { + body: { + has_setup: true, + has_data: false, + pre_8_9_1_data: true, + }, + }).as('getEsResources'); + cy.visitKibana('/app/profiling'); + cy.wait('@getEsResources'); + cy.contains('Delete existing profiling data'); + }); + }); }); diff --git a/x-pack/plugins/profiling/e2e/cypress/e2e/profiling_views/home.cy.ts b/x-pack/plugins/profiling/e2e/cypress/e2e/profiling_views/home.cy.ts index 1f4f91156f797..215738872c7d5 100644 --- a/x-pack/plugins/profiling/e2e/cypress/e2e/profiling_views/home.cy.ts +++ b/x-pack/plugins/profiling/e2e/cypress/e2e/profiling_views/home.cy.ts @@ -19,6 +19,21 @@ describe('Home page', () => { cy.loginAsElastic(); }); + it('opens Profiling UI when user does not have privileges', () => { + cy.intercept('GET', '/internal/profiling/setup/es_resources', { + body: { + has_setup: true, + pre_8_9_1_data: false, + has_data: true, + unauthorized: true, + }, + }).as('getEsResources'); + cy.visitKibana('/app/profiling', { rangeFrom, rangeTo }); + cy.wait('@getEsResources'); + cy.contains('Top 46'); + cy.contains('User privilege limitation'); + }); + it('navigates through the tabs', () => { cy.visitKibana('/app/profiling', { rangeFrom, rangeTo }); cy.url().should('include', '/app/profiling/stacktraces/threads'); diff --git a/x-pack/plugins/profiling/e2e/cypress/fixtures/es_resources_data_false.json b/x-pack/plugins/profiling/e2e/cypress/fixtures/es_resources_data_false.json deleted file mode 100644 index ee5725bcf460d..0000000000000 --- a/x-pack/plugins/profiling/e2e/cypress/fixtures/es_resources_data_false.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "has_setup": true, - "has_data": false -} diff --git a/x-pack/plugins/profiling/e2e/cypress/fixtures/es_resources_setup_false.json b/x-pack/plugins/profiling/e2e/cypress/fixtures/es_resources_setup_false.json deleted file mode 100644 index e86b5b846d908..0000000000000 --- a/x-pack/plugins/profiling/e2e/cypress/fixtures/es_resources_setup_false.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "has_setup": false, - "has_data": false -} diff --git a/x-pack/plugins/profiling/public/app.tsx b/x-pack/plugins/profiling/public/app.tsx index 01bd240dd4028..8ffc064359e3d 100644 --- a/x-pack/plugins/profiling/public/app.tsx +++ b/x-pack/plugins/profiling/public/app.tsx @@ -25,6 +25,7 @@ import { ProfilingPluginPublicSetupDeps, ProfilingPluginPublicStartDeps } from ' import { ProfilingHeaderActionMenu } from './components/profiling_header_action_menu'; import { RouterErrorBoundary } from './routing/router_error_boundary'; import { LicenseProvider } from './components/contexts/license/license_context'; +import { ProfilingSetupStatusContextProvider } from './components/contexts/profiling_setup_status/profiling_setup_status_context'; interface Props { profilingFetchServices: Services; @@ -89,21 +90,23 @@ function App({ - - <> - - - - - - - - - - + + + <> + + + + + + + + + + + diff --git a/x-pack/plugins/profiling/public/components/check_setup.tsx b/x-pack/plugins/profiling/public/components/check_setup.tsx index e5b7993845323..cec2cc6f81c96 100644 --- a/x-pack/plugins/profiling/public/components/check_setup.tsx +++ b/x-pack/plugins/profiling/public/components/check_setup.tsx @@ -26,12 +26,14 @@ import { useLicenseContext } from './contexts/license/use_license_context'; import { useProfilingDependencies } from './contexts/profiling_dependencies/use_profiling_dependencies'; import { LicensePrompt } from './license_prompt'; import { ProfilingAppPageTemplate } from './profiling_app_page_template'; +import { useProfilingSetupStatus } from './contexts/profiling_setup_status/use_profiling_setup_status'; export function CheckSetup({ children }: { children: React.ReactElement }) { const { start: { core }, services: { fetchHasSetup, postSetupResources }, } = useProfilingDependencies(); + const { setProfilingSetupStatus } = useProfilingSetupStatus(); const license = useLicenseContext(); const router = useProfilingRouter(); const history = useHistory(); @@ -47,6 +49,10 @@ export function CheckSetup({ children }: { children: React.ReactElement }) { [fetchHasSetup] ); + if (status === AsyncStatus.Settled) { + setProfilingSetupStatus(data); + } + const http = useAutoAbortedHttpClient([]); if (!license?.hasAtLeast('enterprise')) { @@ -79,7 +85,10 @@ export function CheckSetup({ children }: { children: React.ReactElement }) { } const displaySetupScreen = - (status === AsyncStatus.Settled && data?.has_setup !== true) || !!error; + (status === AsyncStatus.Settled && + data?.has_setup !== true && + data?.pre_8_9_1_data === false) || + !!error; if (displaySetupScreen) { return ( @@ -193,19 +202,27 @@ export function CheckSetup({ children }: { children: React.ReactElement }) { ); } - const displayAddDataInstructions = - status === AsyncStatus.Settled && data?.has_setup === true && data?.has_data === false; - const displayUi = // Display UI if there's data or if the user is opening the add data instruction page. // does not use profiling router because that breaks as at this point the route might not have all required params - data?.has_data === true || history.location.pathname === '/add-data-instructions'; + (data?.has_data === true && data?.pre_8_9_1_data === false) || + history.location.pathname === '/add-data-instructions' || + history.location.pathname === '/delete_data_instructions'; if (displayUi) { return children; } - if (displayAddDataInstructions) { + if (data?.pre_8_9_1_data === true) { + // If the cluster still has data pre 8.9.1 version, redirect to deleting instructions + router.push('/delete_data_instructions', { + path: {}, + query: {}, + }); + return null; + } + + if (status === AsyncStatus.Settled && data?.has_setup === true && data?.has_data === false) { // when there's no data redirect the user to the add data instructions page router.push('/add-data-instructions', { path: {}, diff --git a/x-pack/plugins/profiling/public/components/contexts/profiling_setup_status/profiling_setup_status_context.tsx b/x-pack/plugins/profiling/public/components/contexts/profiling_setup_status/profiling_setup_status_context.tsx new file mode 100644 index 0000000000000..5f02a0886f7c2 --- /dev/null +++ b/x-pack/plugins/profiling/public/components/contexts/profiling_setup_status/profiling_setup_status_context.tsx @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState } from 'react'; +import { ProfilingSetupStatus } from '../../../services'; + +export const ProfilingSetupStatusContext = React.createContext< + | { + profilingSetupStatus: ProfilingSetupStatus | undefined; + setProfilingSetupStatus: React.Dispatch< + React.SetStateAction + >; + } + | undefined +>(undefined); + +export function ProfilingSetupStatusContextProvider({ + children, +}: { + children: React.ReactElement; +}) { + const [profilingSetupStatus, setProfilingSetupStatus] = useState< + ProfilingSetupStatus | undefined + >(); + + return ( + + {children} + + ); +} diff --git a/x-pack/plugins/profiling/public/components/contexts/profiling_setup_status/use_profiling_setup_status.tsx b/x-pack/plugins/profiling/public/components/contexts/profiling_setup_status/use_profiling_setup_status.tsx new file mode 100644 index 0000000000000..ac2e4379a9864 --- /dev/null +++ b/x-pack/plugins/profiling/public/components/contexts/profiling_setup_status/use_profiling_setup_status.tsx @@ -0,0 +1,17 @@ +/* + * 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 { useContext } from 'react'; +import { ProfilingSetupStatusContext } from './profiling_setup_status_context'; + +export function useProfilingSetupStatus() { + const context = useContext(ProfilingSetupStatusContext); + if (!context) { + throw new Error('ProfilingSetupStatusContext not found'); + } + return context; +} diff --git a/x-pack/plugins/profiling/public/components/profiling_app_page_template/index.tsx b/x-pack/plugins/profiling/public/components/profiling_app_page_template/index.tsx index 061215abe6ccd..a62d342b3919e 100644 --- a/x-pack/plugins/profiling/public/components/profiling_app_page_template/index.tsx +++ b/x-pack/plugins/profiling/public/components/profiling_app_page_template/index.tsx @@ -8,6 +8,7 @@ import { EuiBetaBadge, EuiButton, + EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, @@ -20,6 +21,8 @@ import { useHistory } from 'react-router-dom'; import { NoDataPageProps } from '@kbn/shared-ux-page-no-data-types'; import { useProfilingDependencies } from '../contexts/profiling_dependencies/use_profiling_dependencies'; import { PrimaryProfilingSearchBar } from './primary_profiling_search_bar'; +import { useLocalStorage } from '../../hooks/use_local_storage'; +import { useProfilingSetupStatus } from '../contexts/profiling_setup_status/use_profiling_setup_status'; export const PROFILING_FEEDBACK_LINK = 'https://ela.st/profiling-feedback'; @@ -46,6 +49,12 @@ export function ProfilingAppPageTemplate({ start: { observabilityShared }, } = useProfilingDependencies(); + const [privilegesWarningDismissed, setPrivilegesWarningDismissed] = useLocalStorage( + 'profiling.privilegesWarningDismissed', + false + ); + const { profilingSetupStatus } = useProfilingSetupStatus(); + const { PageTemplate: ObservabilityPageTemplate } = observabilityShared.navigation; const history = useHistory(); @@ -110,6 +119,32 @@ export function ProfilingAppPageTemplate({ )} + {profilingSetupStatus?.unauthorized === true && privilegesWarningDismissed !== true ? ( + + +

    + {i18n.translate('xpack.profiling.privilegesWarningDescription', { + defaultMessage: + 'Due to privileges issues we could not check the Universal Profiling status. If you encounter any issues or if data fails to load, please contact your administrator for assistance.', + })} +

    + { + setPrivilegesWarningDismissed(true); + }} + > + {i18n.translate('xpack.profiling.dismissPrivilegesCallout', { + defaultMessage: 'Dismiss', + })} + +
    +
    + ) : null} {children} diff --git a/x-pack/plugins/profiling/public/hooks/use_local_storage.ts b/x-pack/plugins/profiling/public/hooks/use_local_storage.ts new file mode 100644 index 0000000000000..5adbb36432127 --- /dev/null +++ b/x-pack/plugins/profiling/public/hooks/use_local_storage.ts @@ -0,0 +1,57 @@ +/* + * 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 { useState, useEffect, useMemo } from 'react'; + +export function useLocalStorage(key: string, defaultValue: T) { + // This is necessary to fix a race condition issue. + // It guarantees that the latest value will be always returned after the value is updated + const [storageUpdate, setStorageUpdate] = useState(0); + + const item = useMemo(() => { + return getFromStorage(key, defaultValue); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [key, storageUpdate, defaultValue]); + + const saveToStorage = (value: T) => { + if (value === undefined) { + window.localStorage.removeItem(key); + } else { + window.localStorage.setItem(key, JSON.stringify(value)); + setStorageUpdate(storageUpdate + 1); + } + }; + + useEffect(() => { + function onUpdate(event: StorageEvent) { + if (event.key === key) { + setStorageUpdate(storageUpdate + 1); + } + } + window.addEventListener('storage', onUpdate); + return () => { + window.removeEventListener('storage', onUpdate); + }; + }, [key, setStorageUpdate, storageUpdate]); + + return [item, saveToStorage] as const; +} + +function getFromStorage(keyName: string, defaultValue: T) { + const storedItem = window.localStorage.getItem(keyName); + + if (storedItem !== null) { + try { + return JSON.parse(storedItem) as T; + } catch (err) { + window.localStorage.removeItem(keyName); + // eslint-disable-next-line no-console + console.log(`Unable to decode: ${keyName}`); + } + } + return defaultValue; +} diff --git a/x-pack/plugins/profiling/public/routing/index.tsx b/x-pack/plugins/profiling/public/routing/index.tsx index db2a22f357580..9e08cfbc53fec 100644 --- a/x-pack/plugins/profiling/public/routing/index.tsx +++ b/x-pack/plugins/profiling/public/routing/index.tsx @@ -27,6 +27,7 @@ import { AddDataTabs, AddDataView } from '../views/add_data_view'; import { StackTracesView } from '../views/stack_traces_view'; import { StorageExplorerView } from '../views/storage_explorer'; import { RouteBreadcrumb } from './route_breadcrumb'; +import { DeleteDataView } from '../views/delete_data_view'; const routes = { '/': { @@ -62,6 +63,9 @@ const routes = { }, }, }, + '/delete_data_instructions': { + element: , + }, '/': { children: { '/stacktraces/{topNType}': { diff --git a/x-pack/plugins/profiling/public/services.ts b/x-pack/plugins/profiling/public/services.ts index a477d522b3417..1a2a684f96e15 100644 --- a/x-pack/plugins/profiling/public/services.ts +++ b/x-pack/plugins/profiling/public/services.ts @@ -18,6 +18,13 @@ import { TopNResponse } from '../common/topn'; import type { SetupDataCollectionInstructions } from '../server/lib/setup/get_setup_instructions'; import { AutoAbortedHttpService } from './hooks/use_auto_aborted_http_client'; +export interface ProfilingSetupStatus { + has_setup: boolean; + has_data: boolean; + pre_8_9_1_data: boolean; + unauthorized?: boolean; +} + export interface Services { fetchTopN: (params: { http: AutoAbortedHttpService; @@ -40,9 +47,7 @@ export interface Services { timeTo: number; kuery: string; }) => Promise; - fetchHasSetup: (params: { - http: AutoAbortedHttpService; - }) => Promise<{ has_setup: boolean; has_data: boolean }>; + fetchHasSetup: (params: { http: AutoAbortedHttpService }) => Promise; postSetupResources: (params: { http: AutoAbortedHttpService }) => Promise; setupDataCollectionInstructions: (params: { http: AutoAbortedHttpService; @@ -101,10 +106,7 @@ export function getServices(): Services { return createFlameGraph(baseFlamegraph); }, fetchHasSetup: async ({ http }) => { - const hasSetup = (await http.get(paths.HasSetupESResources, {})) as { - has_setup: boolean; - has_data: boolean; - }; + const hasSetup = (await http.get(paths.HasSetupESResources, {})) as ProfilingSetupStatus; return hasSetup; }, postSetupResources: async ({ http }) => { diff --git a/x-pack/plugins/profiling/public/views/delete_data_view/index.tsx b/x-pack/plugins/profiling/public/views/delete_data_view/index.tsx new file mode 100644 index 0000000000000..7e07ba4a1ec86 --- /dev/null +++ b/x-pack/plugins/profiling/public/views/delete_data_view/index.tsx @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiCard, EuiIcon, EuiLink } from '@elastic/eui'; +import React from 'react'; +import { useProfilingDependencies } from '../../components/contexts/profiling_dependencies/use_profiling_dependencies'; +import { ProfilingAppPageTemplate } from '../../components/profiling_app_page_template'; + +export function DeleteDataView() { + const { + start: { + core: { docLinks }, + }, + } = useProfilingDependencies(); + + return ( + +
    + } + title="You have existing profiling data" + description="To proceed with the Universal Profiling setup, please delete existing profiling data following the steps described in the link below." + footer={ +
    + + Delete existing profiling data + +
    + } + /> +
    +
    + ); +} diff --git a/x-pack/plugins/profiling/server/lib/setup/cluster_settings.ts b/x-pack/plugins/profiling/server/lib/setup/cluster_settings.ts index 72d84b329ab2d..dde0b33da6291 100644 --- a/x-pack/plugins/profiling/server/lib/setup/cluster_settings.ts +++ b/x-pack/plugins/profiling/server/lib/setup/cluster_settings.ts @@ -42,6 +42,7 @@ export async function validateResourceManagement({ }, resources: { created: statusResponse.resources.created, + pre_8_9_1_data: statusResponse.resources.pre_8_9_1_data, }, }; } diff --git a/x-pack/plugins/profiling/server/routes/setup.ts b/x-pack/plugins/profiling/server/routes/setup.ts index 4e4e2c15b9f66..1ce7d8d056201 100644 --- a/x-pack/plugins/profiling/server/routes/setup.ts +++ b/x-pack/plugins/profiling/server/routes/setup.ts @@ -9,8 +9,7 @@ import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; import { RouteRegisterParameters } from '.'; import { getRoutePaths } from '../../common'; import { - areResourcesSetupForAdmin, - areResourcesSetupForViewer, + areResourcesSetup, createDefaultSetupState, mergePartialSetupStates, } from '../../common/setup'; @@ -88,58 +87,47 @@ export function registerSetupRoute({ }, }); } - - const verifyFunctionsForViewer = [ + const verifyFunctions = [ + validateMaximumBuckets, + validateResourceManagement, + validateSecurityRole, validateCollectorPackagePolicy, validateSymbolizerPackagePolicy, validateProfilingInApmPackagePolicy, ]; - const partialStatesForViewer = await Promise.all([ - ...verifyFunctionsForViewer.map((fn) => fn(setupOptions)), + const partialStates = await Promise.all([ + ...verifyFunctions.map((fn) => fn(setupOptions)), hasProfilingData({ ...setupOptions, client: clientWithProfilingAuth, }), ]); - const mergedStateForViewer = mergePartialSetupStates(state, partialStatesForViewer); - - /* - * We need to split the verification steps - * because of users with viewer privileges - * cannot get the cluster settings - */ - if (areResourcesSetupForViewer(mergedStateForViewer)) { - return response.ok({ - body: { - has_setup: true, - has_data: mergedStateForViewer.data.available, - }, - }); - } - - /** - * Performe advanced verification in case the first step failed. - */ - const verifyFunctionsForAdmin = [ - validateMaximumBuckets, - validateResourceManagement, - validateSecurityRole, - ]; - - const partialStatesForAdmin = await Promise.all( - verifyFunctionsForAdmin.map((fn) => fn(setupOptions)) - ); - const mergedState = mergePartialSetupStates(mergedStateForViewer, partialStatesForAdmin); + const mergedState = mergePartialSetupStates(state, partialStates); return response.ok({ body: { - has_setup: areResourcesSetupForAdmin(mergedState), + has_setup: areResourcesSetup(mergedState), has_data: mergedState.data.available, + pre_8_9_1_data: mergedState.resources.pre_8_9_1_data, }, }); } catch (error) { + // We cannot fully check the status of all resources + // to make sure Profiling has been set up and has data + // for users with monitor privileges. This privileges + // is needed to call the profiling ES plugin for example. + if (error?.meta?.statusCode === 403) { + return response.ok({ + body: { + has_setup: true, + pre_8_9_1_data: false, + has_data: true, + unauthorized: true, + }, + }); + } return handleRouteHandlerError({ error, logger, @@ -192,32 +180,36 @@ export function registerSetupRoute({ const partialStates = await Promise.all( [ - validateCollectorPackagePolicy, - validateMaximumBuckets, validateResourceManagement, validateSecurityRole, + validateMaximumBuckets, + validateCollectorPackagePolicy, validateSymbolizerPackagePolicy, validateProfilingInApmPackagePolicy, ].map((fn) => fn(setupOptions)) ); const mergedState = mergePartialSetupStates(state, partialStates); - const executeFunctions = [ + const executeAdminFunctions = [ + ...(mergedState.resource_management.enabled ? [] : [enableResourceManagement]), + ...(mergedState.permissions.configured ? [] : [setSecurityRole]), + ...(mergedState.settings.configured ? [] : [setMaximumBuckets]), + ]; + + const executeViewerFunctions = [ ...(mergedState.policies.collector.installed ? [] : [createCollectorPackagePolicy]), ...(mergedState.policies.symbolizer.installed ? [] : [createSymbolizerPackagePolicy]), ...(mergedState.policies.apm.profilingEnabled ? [removeProfilingFromApmPackagePolicy] : []), - ...(mergedState.resource_management.enabled ? [] : [enableResourceManagement]), - ...(mergedState.permissions.configured ? [] : [setSecurityRole]), - ...(mergedState.settings.configured ? [] : [setMaximumBuckets]), ]; - if (!executeFunctions.length) { + if (!executeAdminFunctions.length && !executeViewerFunctions.length) { return response.ok(); } - await Promise.all(executeFunctions.map((fn) => fn(setupOptions))); + await Promise.all(executeAdminFunctions.map((fn) => fn(setupOptions))); + await Promise.all(executeViewerFunctions.map((fn) => fn(setupOptions))); if (dependencies.telemetryUsageCounter) { dependencies.telemetryUsageCounter.incrementCounter({ diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.ts index db46af9091be1..9e0035e3e972b 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.ts @@ -7,7 +7,7 @@ import * as t from 'io-ts'; import { listArray } from '@kbn/securitysolution-io-ts-list-types'; -import { NonEmptyString, version, UUID } from '@kbn/securitysolution-io-ts-types'; +import { NonEmptyString, version, UUID, NonEmptyArray } from '@kbn/securitysolution-io-ts-types'; import { max_signals, threat } from '@kbn/securitysolution-io-ts-alerting-types'; export type RuleObjectId = t.TypeOf; @@ -55,14 +55,6 @@ export const RuleAuthorArray = t.array(t.string); // should be non-empty strings export type RuleFalsePositiveArray = t.TypeOf; export const RuleFalsePositiveArray = t.array(t.string); // should be non-empty strings? -/** - * User defined fields to display in areas such as alert details and exceptions auto-populate - * Field added in PR - https://github.com/elastic/kibana/pull/163235 - * @example const investigationFields: RuleCustomHighlightedFieldArray = ['host.os.name'] - */ -export type RuleCustomHighlightedFieldArray = t.TypeOf; -export const RuleCustomHighlightedFieldArray = t.array(NonEmptyString); - export type RuleReferenceArray = t.TypeOf; export const RuleReferenceArray = t.array(t.string); // should be non-empty strings? @@ -265,3 +257,32 @@ export const RelatedIntegration = t.exact( */ export type RelatedIntegrationArray = t.TypeOf; export const RelatedIntegrationArray = t.array(RelatedIntegration); + +/** + * Schema for fields relating to investigation fields, these are user defined fields we use to highlight + * in various features in the UI such as alert details flyout and exceptions auto-population from alert. + * Added in PR #163235 + * Right now we only have a single field but anticipate adding more related fields to store various + * configuration states such as `override` - where a user might say if they want only these fields to + * display, or if they want these fields + the fields we select. When expanding this field, it may look + * something like: + * export const investigationFields = t.intersection([ + * t.exact( + * t.type({ + * field_names: NonEmptyArray(NonEmptyString), + * }) + * ), + * t.exact( + * t.partial({ + * overide: t.boolean, + * }) + * ), + * ]); + * + */ +export type InvestigationFields = t.TypeOf; +export const InvestigationFields = t.exact( + t.type({ + field_names: NonEmptyArray(NonEmptyString), + }) +); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_request_schema.test.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_request_schema.test.ts index 9d15c355df60d..4d0e522ed734c 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_request_schema.test.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_request_schema.test.ts @@ -1290,10 +1290,38 @@ describe('rules schema', () => { expect(getPaths(left(message.errors))).toEqual(['invalid keys "data_view_id"']); }); - test('You can optionally send in an array of investigation_fields', () => { + test('You can omit investigation_fields', () => { + // getCreateRulesSchemaMock doesn't include investigation_fields + const payload: RuleCreateProps = getCreateRulesSchemaMock(); + + const decoded = RuleCreateProps.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('You cannot pass empty object for investigation_fields', () => { + const payload: Omit & { + investigation_fields: unknown; + } = { + ...getCreateRulesSchemaMock(), + investigation_fields: {}, + }; + + const decoded = RuleCreateProps.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "investigation_fields,field_names"', + ]); + expect(message.schema).toEqual({}); + }); + + test('You can send in investigation_fields', () => { const payload: RuleCreateProps = { ...getCreateRulesSchemaMock(), - investigation_fields: ['field1', 'field2'], + investigation_fields: { field_names: ['field1', 'field2'] }, }; const decoded = RuleCreateProps.decode(payload); @@ -1303,19 +1331,49 @@ describe('rules schema', () => { expect(message.schema).toEqual(payload); }); - test('You cannot send in an array of investigation_fields that are numbers', () => { + test('You cannot send in an empty array of investigation_fields.field_names', () => { + const payload = { + ...getCreateRulesSchemaMock(), + investigation_fields: { field_names: [] }, + }; + + const decoded = RuleCreateProps.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "[]" supplied to "investigation_fields,field_names"', + ]); + expect(message.schema).toEqual({}); + }); + + test('You cannot send in an array of investigation_fields.field_names that are numbers', () => { + const payload = { + ...getCreateRulesSchemaMock(), + investigation_fields: { field_names: [0, 1, 2] }, + }; + + const decoded = RuleCreateProps.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "0" supplied to "investigation_fields,field_names"', + 'Invalid value "1" supplied to "investigation_fields,field_names"', + 'Invalid value "2" supplied to "investigation_fields,field_names"', + ]); + expect(message.schema).toEqual({}); + }); + + test('You cannot send in investigation_fields without specifying fields', () => { const payload = { ...getCreateRulesSchemaMock(), - investigation_fields: [0, 1, 2], + investigation_fields: { foo: true }, }; const decoded = RuleCreateProps.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "0" supplied to "investigation_fields"', - 'Invalid value "1" supplied to "investigation_fields"', - 'Invalid value "2" supplied to "investigation_fields"', + 'Invalid value "undefined" supplied to "investigation_fields,field_names"', ]); expect(message.schema).toEqual({}); }); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_response_schema.test.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_response_schema.test.ts index cd1562b1c4c48..da5ebfc27771e 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_response_schema.test.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_response_schema.test.ts @@ -234,47 +234,73 @@ describe('Rule response schema', () => { }); describe('investigation_fields', () => { - test('it should validate rule with empty array for "investigation_fields"', () => { + test('it should validate rule with "investigation_fields"', () => { const payload = getRulesSchemaMock(); - payload.investigation_fields = []; + payload.investigation_fields = { field_names: ['foo', 'bar'] }; const decoded = RuleResponse.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); - const expected = { ...getRulesSchemaMock(), investigation_fields: [] }; + const expected = { + ...getRulesSchemaMock(), + investigation_fields: { field_names: ['foo', 'bar'] }, + }; expect(getPaths(left(message.errors))).toEqual([]); expect(message.schema).toEqual(expected); }); - test('it should validate rule with "investigation_fields"', () => { - const payload = getRulesSchemaMock(); - payload.investigation_fields = ['foo', 'bar']; + test('it should validate undefined for "investigation_fields"', () => { + const payload: RuleResponse = { + ...getRulesSchemaMock(), + investigation_fields: undefined, + }; const decoded = RuleResponse.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); - const expected = { ...getRulesSchemaMock(), investigation_fields: ['foo', 'bar'] }; + const expected = { ...getRulesSchemaMock(), investigation_fields: undefined }; expect(getPaths(left(message.errors))).toEqual([]); expect(message.schema).toEqual(expected); }); - test('it should validate undefined for "investigation_fields"', () => { + test('it should validate "investigation_fields" not in schema', () => { const payload: RuleResponse = { ...getRulesSchemaMock(), investigation_fields: undefined, }; + delete payload.investigation_fields; + const decoded = RuleResponse.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); - const expected = { ...getRulesSchemaMock(), investigation_fields: undefined }; + const expected = getRulesSchemaMock(); expect(getPaths(left(message.errors))).toEqual([]); expect(message.schema).toEqual(expected); }); + test('it should NOT validate an empty array for "investigation_fields.field_names"', () => { + const payload: RuleResponse = { + ...getRulesSchemaMock(), + investigation_fields: { + field_names: [], + }, + }; + + const decoded = RuleResponse.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "[]" supplied to "investigation_fields,field_names"', + 'Invalid value "{"field_names":[]}" supplied to "investigation_fields"', + ]); + expect(message.schema).toEqual({}); + }); + test('it should NOT validate a string for "investigation_fields"', () => { const payload: Omit & { investigation_fields: string; diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.ts index acc429eb2f340..fd9f25b19cc0d 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.ts @@ -53,7 +53,7 @@ import { RelatedIntegrationArray, RequiredFieldArray, RuleAuthorArray, - RuleCustomHighlightedFieldArray, + InvestigationFields, RuleDescription, RuleFalsePositiveArray, RuleFilterArray, @@ -117,7 +117,7 @@ export const baseSchema = buildRuleSchemas({ output_index: AlertsIndex, namespace: AlertsIndexNamespace, meta: RuleMetadata, - investigation_fields: RuleCustomHighlightedFieldArray, + investigation_fields: InvestigationFields, // Throttle throttle: RuleActionThrottle, }, diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts index b06a5532ec12c..cbc84c3314e88 100644 --- a/x-pack/plugins/security_solution/common/experimental_features.ts +++ b/x-pack/plugins/security_solution/common/experimental_features.ts @@ -89,15 +89,6 @@ export const allowedExperimentalValues = Object.freeze({ **/ newUserDetailsFlyout: false, - /** - * Enables Protections/Detections Coverage Overview page (Epic link https://github.com/elastic/security-team/issues/2905) - * - * This flag aims to facilitate the development process as the feature may not make it to 8.10 release. - * - * The flag doesn't have to be documented and has to be removed after the feature is ready to release. - */ - detectionsCoverageOverview: true, - /** * Enable risk engine client and initialisation of datastream, component templates and mappings */ diff --git a/x-pack/plugins/security_solution/kibana.jsonc b/x-pack/plugins/security_solution/kibana.jsonc index f4716adfca310..91f366b5dd94f 100644 --- a/x-pack/plugins/security_solution/kibana.jsonc +++ b/x-pack/plugins/security_solution/kibana.jsonc @@ -22,6 +22,7 @@ "dataViews", "discover", "ecsDataQualityDashboard", + "elasticAssistant", "embeddable", "eventLog", "features", diff --git a/x-pack/plugins/security_solution/public/actions/constants.ts b/x-pack/plugins/security_solution/public/actions/constants.ts index ad8b3da853396..95c5ef2d788d2 100644 --- a/x-pack/plugins/security_solution/public/actions/constants.ts +++ b/x-pack/plugins/security_solution/public/actions/constants.ts @@ -10,6 +10,14 @@ export enum SecurityCellActionsTrigger { ALERTS_COUNT = 'security-alertsCount-cellActions', } +export enum DiscoverInTimelineTrigger { + HISTOGRAM_TRIGGER = 'security-discoverInTimeline-histogramTrigger', +} + +export enum DiscoverInTimelineAction { + VIS_FILTER_ACTION = 'security-discoverInTimeline-visFilterAction', +} + export enum SecurityCellActionType { FILTER = 'security-cellAction-type-filter', COPY = 'security-cellAction-type-copyToClipboard', diff --git a/x-pack/plugins/security_solution/public/actions/discover_in_timeline/vis_apply_filter.ts b/x-pack/plugins/security_solution/public/actions/discover_in_timeline/vis_apply_filter.ts new file mode 100644 index 0000000000000..5b6d2f547c491 --- /dev/null +++ b/x-pack/plugins/security_solution/public/actions/discover_in_timeline/vis_apply_filter.ts @@ -0,0 +1,54 @@ +/* + * 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 { createFilterAction } from '@kbn/unified-search-plugin/public'; +import type { History } from 'history'; +import type { SecurityAppStore } from '../../common/store'; +import type { StartServices } from '../../types'; +import { DiscoverInTimelineTrigger, DiscoverInTimelineAction } from '../constants'; + +const createDiscoverHistogramCustomFilterAction = ( + store: SecurityAppStore, + history: History, + services: StartServices +) => { + const histogramApplyFilter = createFilterAction( + services.customDataService.query.filterManager, + services.customDataService.query.timefilter.timefilter, + services.theme, + DiscoverInTimelineAction.VIS_FILTER_ACTION, + DiscoverInTimelineAction.VIS_FILTER_ACTION + ); + services.uiActions.registerAction(histogramApplyFilter); + + return histogramApplyFilter; +}; + +const createDiscoverHistogramCustomTrigger = ( + store: SecurityAppStore, + history: History, + services: StartServices +) => { + services.uiActions.registerTrigger({ + id: DiscoverInTimelineTrigger.HISTOGRAM_TRIGGER, + }); +}; + +export const registerDiscoverHistogramActions = ( + store: SecurityAppStore, + history: History, + services: StartServices +) => { + createDiscoverHistogramCustomTrigger(store, history, services); + + const histogramApplyFilter = createDiscoverHistogramCustomFilterAction(store, history, services); + + services.uiActions.attachAction( + DiscoverInTimelineTrigger.HISTOGRAM_TRIGGER, + histogramApplyFilter.id + ); +}; diff --git a/x-pack/plugins/security_solution/public/actions/register.ts b/x-pack/plugins/security_solution/public/actions/register.ts index fc0267243c455..33fa2f0d3baac 100644 --- a/x-pack/plugins/security_solution/public/actions/register.ts +++ b/x-pack/plugins/security_solution/public/actions/register.ts @@ -37,6 +37,7 @@ import type { SecurityCellActions, } from './types'; import { enhanceActionWithTelemetry } from './telemetry'; +import { registerDiscoverHistogramActions } from './discover_in_timeline/vis_apply_filter'; export const registerUIActions = ( store: SecurityAppStore, @@ -46,6 +47,7 @@ export const registerUIActions = ( registerLensEmbeddableActions(store, services); registerDiscoverCellActions(store, services); registerCellActions(store, history, services); + registerDiscoverHistogramActions(store, history, services); }; const registerLensEmbeddableActions = (store: SecurityAppStore, services: StartServices) => { diff --git a/x-pack/plugins/security_solution/public/actions/types.ts b/x-pack/plugins/security_solution/public/actions/types.ts index a4c992632b7d6..582c63c467360 100644 --- a/x-pack/plugins/security_solution/public/actions/types.ts +++ b/x-pack/plugins/security_solution/public/actions/types.ts @@ -7,6 +7,7 @@ import type { CellAction, CellActionExecutionContext, CellActionFactory } from '@kbn/cell-actions'; import type { QueryOperator } from '../../common/types'; +export { DiscoverInTimelineAction, DiscoverInTimelineTrigger } from './constants'; export interface AndFilter { field: string; value: string | string[]; diff --git a/x-pack/plugins/security_solution/public/app/app.tsx b/x-pack/plugins/security_solution/public/app/app.tsx index 3e172207fb4bd..8bbf2c3a425ec 100644 --- a/x-pack/plugins/security_solution/public/app/app.tsx +++ b/x-pack/plugins/security_solution/public/app/app.tsx @@ -99,6 +99,7 @@ const StartAppComponent: FC = ({ actionTypeRegistry={actionTypeRegistry} augmentMessageCodeBlocks={augmentMessageCodeBlocks} assistantAvailability={assistantAvailability} + assistantLangChain={false} assistantTelemetry={assistantTelemetry} defaultAllow={defaultAllow} defaultAllowReplacement={defaultAllowReplacement} diff --git a/x-pack/plugins/security_solution/public/cases/links.ts b/x-pack/plugins/security_solution/public/cases/links.ts index 54d20b42d553a..59916790fb9f7 100644 --- a/x-pack/plugins/security_solution/public/cases/links.ts +++ b/x-pack/plugins/security_solution/public/cases/links.ts @@ -18,7 +18,7 @@ const casesLinks = getCasesDeepLinks({ basePath: CASES_PATH, extend: { [SecurityPageName.case]: { - globalNavPosition: 4, + globalNavPosition: 5, capabilities: [`${CASES_FEATURE_ID}.${READ_CASES_CAPABILITY}`], }, [SecurityPageName.caseConfigure]: { diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/links.ts b/x-pack/plugins/security_solution/public/cloud_security_posture/links.ts index 9b1483174adc7..4b2bc5f289dba 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/links.ts +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/links.ts @@ -20,7 +20,7 @@ const commonLinkProperties: Partial = { export const findingsLinks: LinkItem = { ...getSecuritySolutionLink('findings'), - globalNavPosition: 3, + globalNavPosition: 4, ...commonLinkProperties, }; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx index 7d0892d3cbf9d..2273389e53733 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx @@ -289,7 +289,7 @@ const EventDetailsComponent: React.FC = ({ isReadOnly, }} goToTable={goToTableTab} - investigationFields={maybeRule?.investigation_fields ?? []} + investigationFields={maybeRule?.investigation_fields?.field_names ?? []} /> ({ and: [], enabled: true, id: escapeDataProviderId(id), name: field, - excluded: false, + excluded, kqlQuery: '', queryMatch: { field, @@ -75,9 +76,10 @@ export const getDataProviderAnd = ( field: string, id: string, value: string | string[], - operator: QueryOperator = IS_OPERATOR + operator: QueryOperator = IS_OPERATOR, + excluded: boolean = false ): DataProvidersAnd => { - const { and, ...dataProvider } = getDataProvider(field, id, value, operator); + const { and, ...dataProvider } = getDataProvider(field, id, value, operator, excluded); return dataProvider; }; diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/use_timelines_events.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/use_timelines_events.tsx index 3a70ccebb9ca6..2b234879ccc50 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/use_timelines_events.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/use_timelines_events.tsx @@ -12,7 +12,7 @@ import { useDispatch } from 'react-redux'; import { Subscription } from 'rxjs'; import type { DataView } from '@kbn/data-views-plugin/public'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import { isCompleteResponse, isErrorResponse } from '@kbn/data-plugin/common'; +import { isCompleteResponse } from '@kbn/data-plugin/common'; import type { Inspect, PaginationInputPaginated, @@ -32,8 +32,6 @@ import type { RunTimeMappings } from '../../store/sourcerer/model'; import { TimelineEventsQueries } from '../../../../common/search_strategy'; import type { KueryFilterQueryKind } from '../../../../common/types'; import type { ESQuery } from '../../../../common/typed_json'; -import { useAppToasts } from '../../hooks/use_app_toasts'; -import { ERROR_TIMELINE_EVENTS } from './translations'; import type { AlertWorkflowStatus } from '../../types'; import { getSearchTransactionName, useStartTransaction } from '../../lib/apm/use_start_transaction'; export type InspectResponse = Inspect & { response: string[] }; @@ -220,7 +218,6 @@ export const useTimelineEventsHandler = ({ loadPage: wrappedLoadPage, updatedAt: 0, }); - const { addWarning } = useAppToasts(); const timelineSearch = useCallback( (request: TimelineRequest | null, onNextHandler?: OnNextResponseHandler) => { @@ -233,7 +230,7 @@ export const useTimelineEventsHandler = ({ abortCtrl.current = new AbortController(); setLoading(true); if (data && data.search) { - const { endTracking } = startTracking(); + startTracking(); const abortSignal = abortCtrl.current.signal; searchSubscription$.current = data.search .search, TimelineResponse>( @@ -270,11 +267,6 @@ export const useTimelineEventsHandler = ({ setFilterStatus(request.filterStatus); setLoading(false); - searchSubscription$.current.unsubscribe(); - } else if (isErrorResponse(response)) { - setLoading(false); - endTracking('invalid'); - addWarning(ERROR_TIMELINE_EVENTS); searchSubscription$.current.unsubscribe(); } }, @@ -292,7 +284,7 @@ export const useTimelineEventsHandler = ({ asyncSearch(); refetch.current = asyncSearch; }, - [skip, data, entityType, dataViewId, addWarning, startTracking, dispatch, id, prevFilterStatus] + [skip, data, entityType, dataViewId, startTracking, dispatch, id, prevFilterStatus] ); useEffect(() => { diff --git a/x-pack/plugins/security_solution/public/common/components/links_to_docs/links_components.tsx b/x-pack/plugins/security_solution/public/common/components/links_to_docs/links_components.tsx index 82d4c5b5a3e27..f5595ac4a826c 100644 --- a/x-pack/plugins/security_solution/public/common/components/links_to_docs/links_components.tsx +++ b/x-pack/plugins/security_solution/public/common/components/links_to_docs/links_components.tsx @@ -29,3 +29,7 @@ export const MlJobCompatibilityLink = () => ( linkText={i18n.ML_JOB_COMPATIBILITY_LINK_TEXT} /> ); + +export const CoverageOverviewLink = () => ( + +); diff --git a/x-pack/plugins/security_solution/public/common/components/links_to_docs/links_translations.ts b/x-pack/plugins/security_solution/public/common/components/links_to_docs/links_translations.ts index a495d95b3cf37..fcb17b6db540f 100644 --- a/x-pack/plugins/security_solution/public/common/components/links_to_docs/links_translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/links_to_docs/links_translations.ts @@ -41,3 +41,11 @@ export const ML_JOB_COMPATIBILITY_LINK_TEXT = i18n.translate( defaultMessage: 'ML job compatibility', } ); + +export const COVERAGE_OVERVIEW_LINK_PATH = 'rules-coverage.html'; +export const COVERAGE_OVERVIEW_LINK_TEXT = i18n.translate( + 'xpack.securitySolution.documentationLinks.coverageOverview.text', + { + defaultMessage: 'Learn more.', + } +); diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/categories.ts b/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/categories.ts index b30ea4844bf03..17e1dca9c8478 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/categories.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/categories.ts @@ -16,6 +16,7 @@ export const CATEGORIES: SeparatorLinkCategory[] = [ { type: LinkCategoryType.separator, linkIds: [ + SecurityPageName.rulesLanding, SecurityPageName.alerts, SecurityPageName.cloudSecurityPostureFindings, SecurityPageName.case, @@ -29,8 +30,4 @@ export const CATEGORIES: SeparatorLinkCategory[] = [ SecurityPageName.exploreLanding, ], }, - { - type: LinkCategoryType.separator, - linkIds: [SecurityPageName.rulesLanding], - }, ]; diff --git a/x-pack/plugins/security_solution/public/common/containers/cti/event_enrichment/api.ts b/x-pack/plugins/security_solution/public/common/containers/cti/event_enrichment/api.ts index bf595de39ee5e..0d08b9a93a5c4 100644 --- a/x-pack/plugins/security_solution/public/common/containers/cti/event_enrichment/api.ts +++ b/x-pack/plugins/security_solution/public/common/containers/cti/event_enrichment/api.ts @@ -9,7 +9,7 @@ import type { Observable } from 'rxjs'; import { filter } from 'rxjs/operators'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import { isErrorResponse, isCompleteResponse } from '@kbn/data-plugin/common'; +import { isCompleteResponse } from '@kbn/data-plugin/common'; import type { CtiEventEnrichmentRequestOptions, CtiEventEnrichmentStrategyResponse, @@ -46,6 +46,4 @@ export const getEventEnrichment = ({ export const getEventEnrichmentComplete = ( props: GetEventEnrichmentProps ): Observable => - getEventEnrichment(props).pipe( - filter((response) => isErrorResponse(response) || isCompleteResponse(response)) - ); + getEventEnrichment(props).pipe(filter((response) => isCompleteResponse(response))); diff --git a/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.ts b/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.ts index b55c566084528..8904ccd5ad8bb 100644 --- a/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.ts +++ b/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.ts @@ -10,7 +10,7 @@ import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; import { Subscription } from 'rxjs'; -import { isCompleteResponse, isErrorResponse } from '@kbn/data-plugin/common'; +import { isCompleteResponse } from '@kbn/data-plugin/common'; import type { inputsModel } from '../../../store'; import { useKibana } from '../../../lib/kibana'; import type { @@ -59,7 +59,7 @@ export const useTimelineLastEventTime = ({ refetch: refetch.current, errorMessage: undefined, }); - const { addError, addWarning } = useAppToasts(); + const { addError } = useAppToasts(); const timelineLastEventTimeSearch = useCallback( (request: TimelineEventsLastEventTimeRequestOptions) => { @@ -85,9 +85,6 @@ export const useTimelineLastEventTime = ({ lastSeen: response.lastSeen, refetch: refetch.current, })); - } else if (isErrorResponse(response)) { - setLoading(false); - addWarning(i18n.ERROR_LAST_EVENT_TIME); } }, error: (msg) => { @@ -107,7 +104,7 @@ export const useTimelineLastEventTime = ({ asyncSearch(); refetch.current = asyncSearch; }, - [data.search, addError, addWarning] + [data.search, addError] ); useEffect(() => { diff --git a/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/translations.ts b/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/translations.ts index c882029958934..840567d5c1f14 100644 --- a/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/translations.ts +++ b/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/translations.ts @@ -7,13 +7,6 @@ import { i18n } from '@kbn/i18n'; -export const ERROR_LAST_EVENT_TIME = i18n.translate( - 'xpack.securitySolution.lastEventTime.errorSearchDescription', - { - defaultMessage: `An error has occurred on last event time search`, - } -); - export const FAIL_LAST_EVENT_TIME = i18n.translate( 'xpack.securitySolution.lastEventTime.failSearchDescription', { diff --git a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.test.ts b/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.test.ts index dba14afd0ff2e..fab3766d2c5d0 100644 --- a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.test.ts +++ b/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.test.ts @@ -190,19 +190,6 @@ describe('useMatrixHistogram', () => { expect(mockEndTracking).toHaveBeenCalledWith('success'); }); - it('should end tracking error when the partial request is invalid', () => { - (useKibana().services.data.search.search as jest.Mock).mockReturnValueOnce({ - subscribe: ({ next }: { next: Function }) => next(null), - }); - - renderHook(useMatrixHistogram, { - initialProps: props, - wrapper: TestProviders, - }); - - expect(mockEndTracking).toHaveBeenCalledWith('invalid'); - }); - it('should end tracking error when the request fails', () => { (useKibana().services.data.search.search as jest.Mock).mockReturnValueOnce({ subscribe: ({ error }: { error: Function }) => error('some error'), diff --git a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts b/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts index f75385fdd4955..137acc4f1c9e7 100644 --- a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts +++ b/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts @@ -10,7 +10,7 @@ import { getOr, noop } from 'lodash/fp'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { Subscription } from 'rxjs'; -import { isErrorResponse, isCompleteResponse } from '@kbn/data-plugin/common'; +import { isCompleteResponse } from '@kbn/data-plugin/common'; import type { MatrixHistogramQueryProps } from '../../components/matrix_histogram/types'; import type { inputsModel } from '../../store'; import { createFilter } from '../helpers'; @@ -92,7 +92,7 @@ export const useMatrixHistogram = ({ ...(isPtrIncluded != null ? { isPtrIncluded } : {}), ...(includeMissingData != null ? { includeMissingData } : {}), }); - const { addError, addWarning } = useAppToasts(); + const { addError } = useAppToasts(); const [matrixHistogramResponse, setMatrixHistogramResponse] = useState({ data: [], @@ -138,11 +138,6 @@ export const useMatrixHistogram = ({ })); endTracking('success'); searchSubscription$.current.unsubscribe(); - } else if (isErrorResponse(response)) { - setLoading(false); - addWarning(i18n.ERROR_MATRIX_HISTOGRAM); - endTracking('invalid'); - searchSubscription$.current.unsubscribe(); } }, error: (msg) => { @@ -160,7 +155,7 @@ export const useMatrixHistogram = ({ asyncSearch(); refetch.current = asyncSearch; }, - [data.search, histogramType, addWarning, addError, errorMessage, startTracking] + [data.search, histogramType, addError, errorMessage, startTracking] ); useEffect(() => { diff --git a/x-pack/plugins/security_solution/public/common/containers/use_search_strategy/index.test.ts b/x-pack/plugins/security_solution/public/common/containers/use_search_strategy/index.test.ts index 00dd75222cafb..6a3b48dce6615 100644 --- a/x-pack/plugins/security_solution/public/common/containers/use_search_strategy/index.test.ts +++ b/x-pack/plugins/security_solution/public/common/containers/use_search_strategy/index.test.ts @@ -273,14 +273,18 @@ describe('useSearchStrategy', () => { expect(mockEndTracking).toBeCalledWith('success'); }); - it('should track invalid search result', () => { - mockResponse.mockReturnValueOnce({}); // mock invalid empty response + it('should handle search error', () => { + mockResponse.mockImplementation(() => { + throw new Error( + 'simulated search response error, which could be 1) undefined response, 2) response without rawResponse, or 3) partial response' + ); + }); const { result } = renderHook(() => useSearch(factoryQueryType)); result.current({ request, abortSignal: new AbortController().signal }); expect(mockStartTracking).toBeCalledTimes(1); - expect(mockEndTracking).toBeCalledWith('invalid'); + expect(mockEndTracking).toBeCalledWith('error'); }); it('should track error search result', () => { @@ -310,14 +314,5 @@ describe('useSearchStrategy', () => { expect(mockEndTracking).toBeCalledTimes(1); expect(mockEndTracking).toBeCalledWith('aborted'); }); - - it('should show toast warning when the API returns partial invalid response', () => { - mockResponse.mockReturnValueOnce({}); // mock invalid empty response - - const { result } = renderHook(() => useSearch(factoryQueryType)); - result.current({ request, abortSignal: new AbortController().signal }); - - expect(mockAddToastWarning).toBeCalled(); - }); }); }); diff --git a/x-pack/plugins/security_solution/public/common/containers/use_search_strategy/index.tsx b/x-pack/plugins/security_solution/public/common/containers/use_search_strategy/index.tsx index 65175f00074c8..b9cd06e77e27e 100644 --- a/x-pack/plugins/security_solution/public/common/containers/use_search_strategy/index.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/use_search_strategy/index.tsx @@ -47,7 +47,6 @@ export const useSearch = ( factoryQueryType: QueryType ): UseSearchFunction => { const { data } = useKibana().services; - const { addWarning } = useAppToasts(); const { startTracking } = useTrackHttpRequest(); const search = useCallback>( @@ -65,16 +64,11 @@ export const useSearch = ( abortSignal, } ) - .pipe(filter((response) => isErrorResponse(response) || isCompleteResponse(response))); + .pipe(filter((response) => isCompleteResponse(response))); observable.subscribe({ next: (response) => { - if (isErrorResponse(response)) { - addWarning(i18n.INVALID_RESPONSE_WARNING_SEARCH_STRATEGY(factoryQueryType)); - endTracking('invalid'); - } else { - endTracking('success'); - } + endTracking('success'); }, error: () => { endTracking(abortSignal.aborted ? 'aborted' : 'error'); @@ -83,7 +77,7 @@ export const useSearch = ( return observable; }, - [addWarning, data.search, factoryQueryType, startTracking] + [data.search, factoryQueryType, startTracking] ); return search; diff --git a/x-pack/plugins/security_solution/public/common/hooks/flyout/use_init_flyout_url_param.ts b/x-pack/plugins/security_solution/public/common/hooks/flyout/use_init_flyout_url_param.ts index fbbf7ea453efd..d6ab7acf0f955 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/flyout/use_init_flyout_url_param.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/flyout/use_init_flyout_url_param.ts @@ -15,12 +15,15 @@ import { tableDefaults, dataTableActions, } from '@kbn/securitysolution-data-table'; +import { ENABLE_EXPANDABLE_FLYOUT_SETTING } from '../../../../common/constants'; import { useInitializeUrlParam } from '../../utils/global_query_string'; import { URL_PARAM_KEY } from '../use_url_state'; import type { FlyoutUrlState } from './types'; import { useShallowEqualSelector } from '../use_selector'; +import { useUiSetting$ } from '../../lib/kibana'; export const useInitFlyoutFromUrlParam = () => { + const [isSecurityFlyoutEnabled] = useUiSetting$(ENABLE_EXPANDABLE_FLYOUT_SETTING); const [urlDetails, setUrlDetails] = useState(null); const [hasLoadedUrlDetails, updateHasLoadedUrlDetails] = useState(false); const dispatch = useDispatch(); @@ -32,16 +35,19 @@ export const useInitFlyoutFromUrlParam = () => { (state) => getDataTable(state, TableId.alertsOnAlertsPage) ?? tableDefaults ); - const onInitialize = useCallback((initialState: FlyoutUrlState | null) => { - if (initialState != null && initialState.panelView) { - setUrlDetails(initialState); - } - }, []); + const onInitialize = useCallback( + (initialState: FlyoutUrlState | null) => { + if (!isSecurityFlyoutEnabled && initialState != null && initialState.panelView) { + setUrlDetails(initialState); + } + }, + [isSecurityFlyoutEnabled] + ); const loadExpandedDetailFromUrl = useCallback(() => { const { initialized, isLoading, totalCount, additionalFilters } = dataTableCurrent; const isTableLoaded = initialized && !isLoading && totalCount > 0; - if (urlDetails) { + if (!isSecurityFlyoutEnabled && urlDetails) { if (!additionalFilters || !additionalFilters.showBuildingBlockAlerts) { // We want to show building block alerts when loading the flyout in case the alert is a building block alert dispatch( @@ -62,7 +68,7 @@ export const useInitFlyoutFromUrlParam = () => { ); } } - }, [dataTableCurrent, dispatch, urlDetails]); + }, [dataTableCurrent, dispatch, isSecurityFlyoutEnabled, urlDetails]); // The alert page creates a default dataTable slice in redux initially that is later overriden when data is retrieved // We use the below to store the urlDetails on app load, and then set it when the table is done loading and has data @@ -72,5 +78,11 @@ export const useInitFlyoutFromUrlParam = () => { } }, [hasLoadedUrlDetails, loadExpandedDetailFromUrl]); - useInitializeUrlParam(URL_PARAM_KEY.eventFlyout, onInitialize); + /** + * The URL_PARAM_KEY.eventFlyout is used for the old flyout as well as the new expandable flyout here: + * x-pack/plugins/security_solution/public/common/hooks/flyout/use_sync_flyout_url_param.ts + * We only want this to run for the old flyout. + */ + const initializeKey = isSecurityFlyoutEnabled ? '' : URL_PARAM_KEY.eventFlyout; + useInitializeUrlParam(initializeKey, onInitialize); }; diff --git a/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts b/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts index 9910470072d2e..92db48980fdda 100644 --- a/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts +++ b/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts @@ -51,6 +51,7 @@ import { of } from 'rxjs'; import { UpsellingService } from '@kbn/security-solution-upselling/service'; import { cloudMock } from '@kbn/cloud-plugin/public/mocks'; import { NavigationProvider } from '@kbn/security-solution-navigation'; +import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks'; const mockUiSettings: Record = { [DEFAULT_TIME_RANGE]: { from: 'now-15m', to: 'now', mode: 'quick' }, @@ -218,6 +219,7 @@ export const createStartServicesMock = ( isSidebarEnabled$: of(true), upselling: new UpsellingService(), customDataService, + uiActions: uiActionsPluginMock.createStartContract(), } as unknown as StartServices; }; diff --git a/x-pack/plugins/security_solution/public/common/mock/global_state.ts b/x-pack/plugins/security_solution/public/common/mock/global_state.ts index 68dcfc049f453..e02dcef6b58ce 100644 --- a/x-pack/plugins/security_solution/public/common/mock/global_state.ts +++ b/x-pack/plugins/security_solution/public/common/mock/global_state.ts @@ -45,6 +45,7 @@ import { UsersFields } from '../../../common/search_strategy/security_solution/u import { initialGroupingState } from '../store/grouping/reducer'; import type { SourcererState } from '../store/sourcerer'; import { EMPTY_RESOLVER } from '../../resolver/store/helpers'; +import { getMockDiscoverInTimelineState } from './mock_discover_state'; const mockFieldMap: DataViewSpec['fields'] = Object.fromEntries( mockIndexFields.map((field) => [field.name, field]) @@ -478,4 +479,5 @@ export const mockGlobalState: State = { * they are cast to mutable versions here. */ management: mockManagementState as ManagementState, + discover: getMockDiscoverInTimelineState(), }; diff --git a/x-pack/plugins/security_solution/public/common/mock/mock_assistant_provider.tsx b/x-pack/plugins/security_solution/public/common/mock/mock_assistant_provider.tsx index f36b776b72b04..90c64ec496c78 100644 --- a/x-pack/plugins/security_solution/public/common/mock/mock_assistant_provider.tsx +++ b/x-pack/plugins/security_solution/public/common/mock/mock_assistant_provider.tsx @@ -34,6 +34,7 @@ export const MockAssistantProviderComponent: React.FC = ({ children }) => [])} baseAllow={[]} baseAllowReplacement={[]} diff --git a/x-pack/plugins/security_solution/public/common/mock/mock_discover_state.ts b/x-pack/plugins/security_solution/public/common/mock/mock_discover_state.ts new file mode 100644 index 0000000000000..f9d851c895ed4 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/mock/mock_discover_state.ts @@ -0,0 +1,25 @@ +/* + * 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 type { SecuritySolutionDiscoverState } from '../store/discover/model'; + +export const getMockDiscoverInTimelineState: () => SecuritySolutionDiscoverState = () => ({ + app: { + query: { + language: 'kuery', + query: '', + }, + sort: [['@timestamp', 'desc']], + columns: ['event.module', 'user.name', 'host.name'], + index: 'security-solution-default', + interval: 'auto', + filters: [], + breakdownField: 'user.name', + }, + internal: undefined, + savedSearch: undefined, +}); diff --git a/x-pack/plugins/security_solution/public/common/store/discover/actions.ts b/x-pack/plugins/security_solution/public/common/store/discover/actions.ts new file mode 100644 index 0000000000000..9226609e8f038 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/store/discover/actions.ts @@ -0,0 +1,23 @@ +/* + * 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 actionCreatorFactory from 'typescript-fsa'; +import type { SecuritySolutionDiscoverState } from './model'; + +const actionCreator = actionCreatorFactory('x-pack/security_solution/discover'); + +export const updateDiscoverAppState = actionCreator<{ + newState: SecuritySolutionDiscoverState['app']; +}>('UPDATE_DISCOVER_APP_STATE'); + +export const updateDiscoverInternalState = actionCreator<{ + newState: SecuritySolutionDiscoverState['internal']; +}>('UPDATE_DISCOVER_INTERNAL_STATE'); + +export const updateDiscoverSavedSearchState = actionCreator<{ + newState: SecuritySolutionDiscoverState['savedSearch']; +}>('UPDATE_DISCOVER_SAVED_SEARCH_STATE'); diff --git a/x-pack/plugins/security_solution/public/common/store/discover/model.ts b/x-pack/plugins/security_solution/public/common/store/discover/model.ts new file mode 100644 index 0000000000000..fa37e3a2c465e --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/store/discover/model.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { DiscoverAppState } from '@kbn/discover-plugin/public/application/main/services/discover_app_state_container'; +import type { InternalState } from '@kbn/discover-plugin/public/application/main/services/discover_internal_state_container'; +import type { SavedSearch } from '@kbn/saved-search-plugin/common'; + +export interface SecuritySolutionDiscoverState { + app: DiscoverAppState | undefined; + internal: InternalState | undefined; + savedSearch: SavedSearch | undefined; +} + +export const initialDiscoverAppState: SecuritySolutionDiscoverState = { + app: undefined, + internal: undefined, + savedSearch: undefined, +}; diff --git a/x-pack/plugins/security_solution/public/common/store/discover/reducer.ts b/x-pack/plugins/security_solution/public/common/store/discover/reducer.ts new file mode 100644 index 0000000000000..90af148f524f5 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/store/discover/reducer.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { reducerWithInitialState } from 'typescript-fsa-reducers'; +import { + updateDiscoverAppState, + updateDiscoverInternalState, + updateDiscoverSavedSearchState, +} from './actions'; +import { initialDiscoverAppState } from './model'; + +export const securitySolutionDiscoverReducer = reducerWithInitialState(initialDiscoverAppState) + .case(updateDiscoverAppState, (state, { newState }) => { + return { + ...state, + app: { + ...state.app, + ...newState, + }, + }; + }) + .case(updateDiscoverInternalState, (state, { newState }) => { + return { + ...state, + internal: newState, + }; + }) + .case(updateDiscoverSavedSearchState, (state, { newState }) => { + return { + ...state, + savedSearch: newState, + }; + }); diff --git a/x-pack/plugins/security_solution/public/common/store/discover/selectors.ts b/x-pack/plugins/security_solution/public/common/store/discover/selectors.ts new file mode 100644 index 0000000000000..8fae4586cfd67 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/store/discover/selectors.ts @@ -0,0 +1,16 @@ +/* + * 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 { createSelector } from 'reselect'; +import type { State } from '../types'; + +export const selectAppState = (state: State) => { + const { discover } = state; + return discover.app; +}; + +export const discoverAppStateSelector = createSelector(selectAppState, (app) => app); diff --git a/x-pack/plugins/security_solution/public/common/store/reducer.ts b/x-pack/plugins/security_solution/public/common/store/reducer.ts index 3e7e64afeaf4e..e60f801537f4b 100644 --- a/x-pack/plugins/security_solution/public/common/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/common/store/reducer.ts @@ -33,6 +33,7 @@ import { globalUrlParamReducer, initialGlobalUrlParam } from './global_url_param import { groupsReducer } from './grouping/reducer'; import type { GroupState } from './grouping/types'; import { analyzerReducer } from '../../resolver/store/reducer'; +import { securitySolutionDiscoverReducer } from './discover/reducer'; import type { AnalyzerState } from '../../resolver/types'; enableMapSet(); @@ -119,6 +120,11 @@ export const createInitialState = ( dataTable: dataTableState.dataTable, groups: groupsState.groups, analyzer: analyzerState.analyzer, + discover: { + app: undefined, + internal: undefined, + savedSearch: undefined, + }, }; return preloadedState; @@ -139,5 +145,6 @@ export const createReducer: ( dataTable: dataTableReducer, groups: groupsReducer, analyzer: analyzerReducer, + discover: securitySolutionDiscoverReducer, ...pluginsReducer, }); diff --git a/x-pack/plugins/security_solution/public/common/store/types.ts b/x-pack/plugins/security_solution/public/common/store/types.ts index 9d8ed515202ca..ec41cdda85ef2 100644 --- a/x-pack/plugins/security_solution/public/common/store/types.ts +++ b/x-pack/plugins/security_solution/public/common/store/types.ts @@ -23,6 +23,7 @@ import type { ManagementPluginState } from '../../management'; import type { UsersPluginState } from '../../explore/users/store'; import type { GlobalUrlParam } from './global_url_param'; import type { GroupState } from './grouping/types'; +import type { SecuritySolutionDiscoverState } from './discover/model'; import type { AnalyzerState } from '../../resolver/types'; export type State = HostsPluginState & @@ -36,6 +37,7 @@ export type State = HostsPluginState & inputs: InputsState; sourcerer: SourcererState; globalUrlParam: GlobalUrlParam; + discover: SecuritySolutionDiscoverState; } & DataTableState & GroupState & AnalyzerState; diff --git a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx index e72e38429f35d..7d579c66e8191 100644 --- a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx +++ b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx @@ -146,6 +146,7 @@ export const DashboardsLandingPage = () => { goToDashboard={goToDashboard} initialFilter={initialFilter} urlStateEnabled={false} + showCreateDashboardButton={false} /> )} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.test.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.test.ts index 5f1a36f931026..86bcebb72ded5 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.test.ts @@ -555,7 +555,7 @@ describe('helpers', () => { severity_mapping: [], tags: ['tag1', 'tag2'], threat: getThreatMock(), - investigation_fields: ['foo', 'bar'], + investigation_fields: { field_names: ['foo', 'bar'] }, }; expect(result).toEqual(expected); @@ -636,7 +636,7 @@ describe('helpers', () => { severity_mapping: [], tags: ['tag1', 'tag2'], threat: getThreatMock(), - investigation_fields: ['foo', 'bar'], + investigation_fields: { field_names: ['foo', 'bar'] }, }; expect(result).toEqual(expected); @@ -661,7 +661,7 @@ describe('helpers', () => { severity_mapping: [], tags: ['tag1', 'tag2'], threat: getThreatMock(), - investigation_fields: ['foo', 'bar'], + investigation_fields: { field_names: ['foo', 'bar'] }, }; expect(result).toEqual(expected); @@ -705,7 +705,7 @@ describe('helpers', () => { severity_mapping: [], tags: ['tag1', 'tag2'], threat: getThreatMock(), - investigation_fields: ['foo', 'bar'], + investigation_fields: { field_names: ['foo', 'bar'] }, }; expect(result).toEqual(expected); @@ -758,7 +758,7 @@ describe('helpers', () => { ], }, ], - investigation_fields: ['foo', 'bar'], + investigation_fields: { field_names: ['foo', 'bar'] }, }; expect(result).toEqual(expected); @@ -787,13 +787,13 @@ describe('helpers', () => { threat: getThreatMock(), timestamp_override: 'event.ingest', timestamp_override_fallback_disabled: true, - investigation_fields: ['foo', 'bar'], + investigation_fields: { field_names: ['foo', 'bar'] }, }; expect(result).toEqual(expected); }); - test('returns formatted object if investigation_fields is empty array', () => { + test('returns formatted object if investigationFields is empty array', () => { const mockStepData: AboutStepRule = { ...mockData, investigationFields: [], @@ -817,7 +817,7 @@ describe('helpers', () => { timestamp_override: undefined, timestamp_override_fallback_disabled: undefined, threat: getThreatMock(), - investigation_fields: [], + investigation_fields: undefined, }; expect(result).toEqual(expected); @@ -843,7 +843,7 @@ describe('helpers', () => { severity_mapping: [], tags: ['tag1', 'tag2'], threat: getThreatMock(), - investigation_fields: ['foo', 'bar'], + investigation_fields: { field_names: ['foo', 'bar'] }, threat_indicator_path: undefined, timestamp_override: undefined, timestamp_override_fallback_disabled: undefined, @@ -855,7 +855,7 @@ describe('helpers', () => { test('returns formatted object if investigation_fields includes empty string', () => { const mockStepData: AboutStepRule = { ...mockData, - investigationFields: [' '], + investigationFields: [' '], }; const result = formatAboutStepData(mockStepData); const expected: AboutStepRuleJson = { @@ -872,7 +872,7 @@ describe('helpers', () => { severity_mapping: [], tags: ['tag1', 'tag2'], threat: getThreatMock(), - investigation_fields: [], + investigation_fields: undefined, threat_indicator_path: undefined, timestamp_override: undefined, timestamp_override_fallback_disabled: undefined, diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts index c3cb11e907a14..c3055adac6ee4 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts @@ -502,6 +502,7 @@ export const formatAboutStepData = ( const detectionExceptionLists = exceptionsList != null ? exceptionsList.filter((list) => list.type !== 'endpoint') : []; + const isinvestigationFieldsEmpty = investigationFields.every((item) => isEmpty(item.trim())); const resp = { author: author.filter((item) => !isEmpty(item)), @@ -525,7 +526,9 @@ export const formatAboutStepData = ( : {}), false_positives: falsePositives.filter((item) => !isEmpty(item)), references: references.filter((item) => !isEmpty(item)), - investigation_fields: investigationFields.filter((item) => !isEmpty(item.trim())), + investigation_fields: isinvestigationFieldsEmpty + ? undefined + : { field_names: investigationFields }, risk_score: riskScore.value, risk_score_mapping: riskScore.isMappingChecked ? riskScore.mapping.filter((m) => m.field != null && m.field !== '') diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/__mocks__/rule_details_context.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/__mocks__/rule_details_context.tsx index f4b04627b5164..a853e8f1422c7 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/__mocks__/rule_details_context.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/__mocks__/rule_details_context.tsx @@ -7,7 +7,7 @@ import React from 'react'; import type { RuleDetailsContextType } from '../rule_details_context'; -import { RuleDetailTabs } from '..'; +import { RuleDetailTabs } from '../use_rule_details_tabs'; export const useRuleDetailsContextMock = { create: (): jest.Mocked => ({ diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_table.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_table.tsx index 9c38520184eef..e30e2b6e6aa52 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_table.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_table.tsx @@ -29,13 +29,13 @@ import { MAX_EXECUTION_EVENTS_DISPLAYED } from '@kbn/securitysolution-rules'; import { mountReactNode } from '@kbn/core-mount-utils-browser-internal'; import { InputsModelId } from '../../../../../common/store/inputs/constants'; -import { RuleDetailTabs } from '..'; + import { RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_METRIC_COLUMNS_STORAGE_KEY } from '../../../../../../common/constants'; import type { RuleExecutionResult, RuleExecutionStatus, } from '../../../../../../common/api/detection_engine/rule_monitoring'; - +import { RuleDetailTabs } from '../use_rule_details_tabs'; import { HeaderSection } from '../../../../../common/components/header_section'; import { UtilityBar, diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.test.tsx deleted file mode 100644 index ed3f06c54e50e..0000000000000 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.test.tsx +++ /dev/null @@ -1,455 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { mount } from 'enzyme'; -import { waitFor } from '@testing-library/react'; -import '../../../../common/mock/match_media'; -import { - createSecuritySolutionStorageMock, - kibanaObservable, - mockGlobalState, - TestProviders, - SUB_PLUGINS_REDUCER, -} from '../../../../common/mock'; -import { RuleDetailsPage } from '.'; -import type { State } from '../../../../common/store'; -import { createStore } from '../../../../common/store'; -import { useUserData } from '../../../../detections/components/user_info'; -import { useRuleWithFallback } from '../../../rule_management/logic/use_rule_with_fallback'; - -import { useSourcererDataView } from '../../../../common/containers/sourcerer'; -import { useParams } from 'react-router-dom'; -import { mockHistory, Router } from '../../../../common/mock/router'; - -import { fillEmptySeverityMappings } from '../../../../detections/pages/detection_engine/rules/helpers'; - -// Test will fail because we will to need to mock some core services to make the test work -// For now let's forget about SiemSearchBar and QueryBar -jest.mock('../../../../common/components/search_bar', () => ({ - SiemSearchBar: () => null, -})); -jest.mock('../../../../detections/pages/detection_engine/rules/helpers', () => { - const original = jest.requireActual( - '../../../../detections/pages/detection_engine/rules/helpers' - ); - return { - ...original, - fillEmptySeverityMappings: jest.fn().mockReturnValue([]), - }; -}); -jest.mock('../../../../common/components/query_bar', () => ({ - QueryBar: () => null, -})); -jest.mock('../../../../detections/containers/detection_engine/lists/use_lists_config'); -jest.mock('../../../../common/components/link_to'); -jest.mock('../../../../detections/components/user_info'); -jest.mock('../../../rule_management/logic/use_rule_with_fallback', () => { - const original = jest.requireActual('../../../rule_management/logic/use_rule_with_fallback'); - return { - ...original, - useRuleWithFallback: jest.fn(), - }; -}); -jest.mock('../../../../common/containers/sourcerer', () => { - const actual = jest.requireActual('../../../../common/containers/sourcerer'); - return { - ...actual, - useSourcererDataView: jest - .fn() - .mockReturnValue({ indexPattern: ['fakeindex'], loading: false }), - }; -}); - -jest.mock('../../../../common/hooks/use_data_table_filters'); - -jest.mock('../../../../common/containers/use_global_time', () => ({ - useGlobalTime: jest.fn().mockReturnValue({ - from: '2020-07-07T08:20:18.966Z', - isInitializing: false, - to: '2020-07-08T08:20:18.966Z', - setQuery: jest.fn(), - }), -})); -jest.mock('react-router-dom', () => { - const originalModule = jest.requireActual('react-router-dom'); - - return { - ...originalModule, - useParams: jest.fn(), - useHistory: jest.fn(), - useLocation: jest.fn().mockReturnValue({ pathname: '/alerts' }), - }; -}); - -// RuleDetailsSnoozeSettings is an isolated component and not essential for existing tests -jest.mock('../../../rule_management/components/rule_snooze_badge', () => ({ - RuleSnoozeBadge: () => <>, -})); - -const mockRedirectLegacyUrl = jest.fn(); -const mockGetLegacyUrlConflict = jest.fn(); -jest.mock('../../../../common/lib/kibana', () => { - const originalModule = jest.requireActual('../../../../common/lib/kibana'); - return { - ...originalModule, - useKibana: () => ({ - services: { - ...originalModule.useKibana().services, - storage: { - get: jest.fn().mockReturnValue(true), - }, - application: { - getUrlForApp: (appId: string, options?: { path?: string }) => - `/app/${appId}${options?.path}`, - navigateToApp: jest.fn(), - capabilities: { - actions: { - delete: true, - save: true, - show: true, - }, - siem: { - 'ai-assistant': true, - }, - }, - }, - data: { - dataViews: { - getIdsWithTitle: async () => - Promise.resolve([{ id: 'myfakeid', title: 'hello*,world*,refreshed*' }]), - create: async ({ title }: { title: string }) => - Promise.resolve({ - id: 'myfakeid', - matchedIndices: ['hello', 'world', 'refreshed'], - fields: [ - { - name: 'bytes', - type: 'number', - esTypes: ['long'], - aggregatable: true, - searchable: true, - count: 10, - readFromDocValues: true, - scripted: false, - isMapped: true, - }, - { - name: 'ssl', - type: 'boolean', - esTypes: ['boolean'], - aggregatable: true, - searchable: true, - count: 20, - readFromDocValues: true, - scripted: false, - isMapped: true, - }, - { - name: '@timestamp', - type: 'date', - esTypes: ['date'], - aggregatable: true, - searchable: true, - count: 30, - readFromDocValues: true, - scripted: false, - isMapped: true, - }, - ], - getIndexPattern: () => title, - getRuntimeMappings: () => ({ - myfield: { - type: 'keyword', - }, - }), - }), - get: async (dataViewId: string, displayErrors?: boolean, refreshFields = false) => - Promise.resolve({ - id: dataViewId, - matchedIndices: refreshFields - ? ['hello', 'world', 'refreshed'] - : ['hello', 'world'], - fields: [ - { - name: 'bytes', - type: 'number', - esTypes: ['long'], - aggregatable: true, - searchable: true, - count: 10, - readFromDocValues: true, - scripted: false, - isMapped: true, - }, - { - name: 'ssl', - type: 'boolean', - esTypes: ['boolean'], - aggregatable: true, - searchable: true, - count: 20, - readFromDocValues: true, - scripted: false, - isMapped: true, - }, - { - name: '@timestamp', - type: 'date', - esTypes: ['date'], - aggregatable: true, - searchable: true, - count: 30, - readFromDocValues: true, - scripted: false, - isMapped: true, - }, - ], - getIndexPattern: () => 'hello*,world*,refreshed*', - getRuntimeMappings: () => ({ - myfield: { - type: 'keyword', - }, - }), - }), - }, - search: { - search: () => ({ - subscribe: () => ({ - unsubscribe: jest.fn(), - }), - }), - }, - }, - spaces: { - ui: { - components: { getLegacyUrlConflict: mockGetLegacyUrlConflict }, - redirectLegacyUrl: mockRedirectLegacyUrl, - }, - }, - }, - }), - }; -}); - -const state: State = { - ...mockGlobalState, -}; - -const mockRule = { - id: 'myfakeruleid', - author: [], - severity_mapping: [], - risk_score_mapping: [], - rule_id: 'rule-1', - risk_score: 50, - description: 'some description', - from: 'now-5m', - to: 'now', - name: 'some-name', - severity: 'low', - type: 'query', - query: 'some query', - index: ['index-1'], - interval: '5m', - references: [], - actions: [], - enabled: false, - false_positives: [], - max_signals: 100, - tags: [], - threat: [], - throttle: null, - version: 1, - exceptions_list: [], -}; -const { storage } = createSecuritySolutionStorageMock(); -const store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - -describe('RuleDetailsPageComponent', () => { - beforeAll(() => { - (useUserData as jest.Mock).mockReturnValue([{}]); - (useParams as jest.Mock).mockReturnValue({}); - (useSourcererDataView as jest.Mock).mockReturnValue({ - indicesExist: true, - indexPattern: {}, - }); - (useRuleWithFallback as jest.Mock).mockReturnValue({ - error: null, - loading: false, - isExistingRule: true, - refresh: jest.fn(), - rule: { ...mockRule }, - }); - (fillEmptySeverityMappings as jest.Mock).mockReturnValue([]); - }); - - beforeEach(() => { - mockRedirectLegacyUrl.mockReset(); - mockGetLegacyUrlConflict.mockReset(); - }); - - it('renders correctly with no outcome property on rule', async () => { - const wrapper = mount( - - - - - - ); - await waitFor(() => { - expect(wrapper.find('[data-test-subj="header-page-title"]').exists()).toBe(true); - expect(mockRedirectLegacyUrl).not.toHaveBeenCalled(); - expect(mockGetLegacyUrlConflict).not.toHaveBeenCalled(); - }); - }); - - it('renders correctly with outcome === "exactMatch"', async () => { - (useRuleWithFallback as jest.Mock).mockReturnValue({ - error: null, - loading: false, - isExistingRule: true, - refresh: jest.fn(), - rule: { ...mockRule, outcome: 'exactMatch' }, - }); - - const wrapper = mount( - - - - - - ); - await waitFor(() => { - expect(wrapper.find('[data-test-subj="header-page-title"]').exists()).toBe(true); - expect(mockRedirectLegacyUrl).not.toHaveBeenCalled(); - expect(mockGetLegacyUrlConflict).not.toHaveBeenCalled(); - }); - }); - - it('renders correctly with outcome === "aliasMatch"', async () => { - (useRuleWithFallback as jest.Mock).mockReturnValue({ - error: null, - loading: false, - isExistingRule: true, - refresh: jest.fn(), - rule: { ...mockRule, outcome: 'aliasMatch', alias_purpose: 'savedObjectConversion' }, - }); - const wrapper = mount( - - - - - - ); - await waitFor(() => { - expect(wrapper.find('[data-test-subj="header-page-title"]').exists()).toBe(true); - expect(mockRedirectLegacyUrl).toHaveBeenCalledWith({ - path: 'rules/id/myfakeruleid', - aliasPurpose: 'savedObjectConversion', - objectNoun: 'rule', - }); - expect(mockGetLegacyUrlConflict).not.toHaveBeenCalled(); - }); - }); - - it('renders correctly when outcome = conflict', async () => { - (useRuleWithFallback as jest.Mock).mockReturnValue({ - error: null, - loading: false, - isExistingRule: true, - refresh: jest.fn(), - rule: { - ...mockRule, - outcome: 'conflict', - alias_target_id: 'aliased_rule_id', - alias_purpose: 'savedObjectConversion', - }, - }); - const wrapper = mount( - - - - - - ); - await waitFor(() => { - expect(wrapper.find('[data-test-subj="header-page-title"]').exists()).toBe(true); - expect(mockRedirectLegacyUrl).not.toHaveBeenCalled(); - expect(mockGetLegacyUrlConflict).toHaveBeenCalledWith({ - currentObjectId: 'myfakeruleid', - objectNoun: 'rule', - otherObjectId: 'aliased_rule_id', - otherObjectPath: `rules/id/aliased_rule_id`, - }); - }); - }); - - it('renders exceptions tab', async () => { - (useRuleWithFallback as jest.Mock).mockReturnValue({ - error: null, - loading: false, - isExistingRule: true, - refresh: jest.fn(), - rule: { - ...mockRule, - outcome: 'conflict', - alias_target_id: 'aliased_rule_id', - alias_purpose: 'savedObjectConversion', - }, - }); - const wrapper = mount( - - - - - - ); - await waitFor(() => { - expect(wrapper.find('[data-test-subj="navigation-rule_exceptions"]').exists()).toBeTruthy(); - expect( - wrapper.find('[data-test-subj="navigation-endpoint_exceptions"]').exists() - ).toBeFalsy(); - }); - }); - - it('renders endpoint exceptions tab when rule includes endpoint list', async () => { - (useRuleWithFallback as jest.Mock).mockReturnValue({ - error: null, - loading: false, - isExistingRule: true, - refresh: jest.fn(), - rule: { - ...mockRule, - outcome: 'conflict', - alias_target_id: 'aliased_rule_id', - alias_purpose: 'savedObjectConversion', - exceptions_list: [ - { - id: 'endpoint_list', - list_id: 'endpoint_list', - type: 'endpoint', - namespace_type: 'agnostic', - }, - ], - }, - }); - const wrapper = mount( - - - - - - ); - await waitFor(() => { - expect(wrapper.find('[data-test-subj="navigation-rule_exceptions"]').exists()).toBeTruthy(); - expect( - wrapper.find('[data-test-subj="navigation-endpoint_exceptions"]').exists() - ).toBeTruthy(); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx index 791db19a92dcd..5f9e1f17dba18 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx @@ -19,10 +19,9 @@ import { EuiWindowEvent, } from '@elastic/eui'; import type { Filter } from '@kbn/es-query'; -import { i18n as i18nTranslate } from '@kbn/i18n'; import { Routes, Route } from '@kbn/shared-ux-router'; -import { noop, omit } from 'lodash/fp'; +import { noop } from 'lodash/fp'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useParams } from 'react-router-dom'; import type { ConnectedProps } from 'react-redux'; @@ -112,13 +111,14 @@ import { RuleStatusFailedCallOut, ruleStatusI18n, } from '../../../../detections/components/rules/rule_execution_status'; -import { ExecutionEventsTable, useRuleExecutionSettings } from '../../../rule_monitoring'; +import { ExecutionEventsTable } from '../../../rule_monitoring'; import { ExecutionLogTable } from './execution_log_table/execution_log_table'; -import * as detectionI18n from '../../../../detections/pages/detection_engine/translations'; import * as ruleI18n from '../../../../detections/pages/detection_engine/rules/translations'; import { RuleDetailsContextProvider } from './rule_details_context'; +// eslint-disable-next-line no-restricted-imports +import { LegacyUrlConflictCallOut } from './legacy_url_conflict_callout'; import { useGetSavedQuery } from '../../../../detections/pages/detection_engine/rules/use_get_saved_query'; import * as i18n from './translations'; import { NeedAdminForUpdateRulesCallOut } from '../../../../detections/components/callouts/need_admin_for_update_callout'; @@ -131,7 +131,6 @@ import { AlertsTableFilterGroup } from '../../../../detections/components/alerts import { useSignalHelpers } from '../../../../common/containers/sourcerer/use_signal_helpers'; import { HeaderPage } from '../../../../common/components/header_page'; import { ExceptionsViewer } from '../../../rule_exceptions/components/all_exception_items_table'; -import type { NavTab } from '../../../../common/components/navigation/types'; import { EditRuleSettingButtonLink } from '../../../../detections/pages/detection_engine/rules/details/components/edit_rule_settings_button_link'; import { useStartMlJobs } from '../../../rule_management/logic/use_start_ml_jobs'; import { useBulkDuplicateExceptionsConfirmation } from '../../../rule_management_ui/components/rules_table/bulk_actions/use_bulk_duplicate_confirmation'; @@ -141,6 +140,9 @@ import { RuleSnoozeBadge } from '../../../rule_management/components/rule_snooze import { useRuleIndexPattern } from '../../../rule_creation_ui/pages/form'; import { DataSourceType } from '../../../../detections/pages/detection_engine/rules/types'; import { useBoolState } from '../../../../common/hooks/use_bool_state'; +// eslint-disable-next-line no-restricted-imports +import { useLegacyUrlRedirect } from './use_redirect_legacy_url'; +import { RuleDetailTabs, useRuleDetailsTabs } from './use_rule_details_tabs'; /** * Need a 100% height here to account for the graph/analyze tool, which sets no explicit height parameters, but fills the available space. @@ -158,22 +160,6 @@ const StyledMinHeightTabContainer = styled.div` min-height: 800px; `; -export enum RuleDetailTabs { - alerts = 'alerts', - exceptions = 'rule_exceptions', - endpointExceptions = 'endpoint_exceptions', - executionResults = 'execution_results', - executionEvents = 'execution_events', -} - -export const RULE_DETAILS_TAB_NAME: Record = { - [RuleDetailTabs.alerts]: detectionI18n.ALERT, - [RuleDetailTabs.exceptions]: i18n.EXCEPTIONS_TAB, - [RuleDetailTabs.endpointExceptions]: i18n.ENDPOINT_EXCEPTIONS_TAB, - [RuleDetailTabs.executionResults]: i18n.EXECUTION_RESULTS_TAB, - [RuleDetailTabs.executionEvents]: i18n.EXECUTION_EVENTS_TAB, -}; - type DetectionEngineComponentProps = PropsFromRedux; const RuleDetailsPageComponent: React.FC = ({ @@ -256,43 +242,7 @@ const RuleDetailsPageComponent: React.FC = ({ await startMlJobs(rule?.machine_learning_job_id); }, [rule, startMlJobs]); - const ruleDetailTabs = useMemo( - (): Record => ({ - [RuleDetailTabs.alerts]: { - id: RuleDetailTabs.alerts, - name: RULE_DETAILS_TAB_NAME[RuleDetailTabs.alerts], - disabled: false, - href: `/rules/id/${ruleId}/${RuleDetailTabs.alerts}`, - }, - [RuleDetailTabs.exceptions]: { - id: RuleDetailTabs.exceptions, - name: RULE_DETAILS_TAB_NAME[RuleDetailTabs.exceptions], - disabled: rule == null, - href: `/rules/id/${ruleId}/${RuleDetailTabs.exceptions}`, - }, - [RuleDetailTabs.endpointExceptions]: { - id: RuleDetailTabs.endpointExceptions, - name: RULE_DETAILS_TAB_NAME[RuleDetailTabs.endpointExceptions], - disabled: rule == null, - href: `/rules/id/${ruleId}/${RuleDetailTabs.endpointExceptions}`, - }, - [RuleDetailTabs.executionResults]: { - id: RuleDetailTabs.executionResults, - name: RULE_DETAILS_TAB_NAME[RuleDetailTabs.executionResults], - disabled: !isExistingRule, - href: `/rules/id/${ruleId}/${RuleDetailTabs.executionResults}`, - }, - [RuleDetailTabs.executionEvents]: { - id: RuleDetailTabs.executionEvents, - name: RULE_DETAILS_TAB_NAME[RuleDetailTabs.executionEvents], - disabled: !isExistingRule, - href: `/rules/id/${ruleId}/${RuleDetailTabs.executionEvents}`, - }, - }), - [isExistingRule, rule, ruleId] - ); - - const [pageTabs, setTabs] = useState>>(ruleDetailTabs); + const pageTabs = useRuleDetailsTabs({ rule, ruleId, isExistingRule, hasIndexRead }); const [isDeleteConfirmationVisible, showDeleteConfirmation, hideDeleteConfirmation] = useBoolState(); @@ -371,74 +321,7 @@ const RuleDetailsPageComponent: React.FC = ({ } }, [maybeRule]); - useEffect(() => { - if (rule) { - const outcome = rule.outcome; - if (spacesApi && outcome === 'aliasMatch') { - // This rule has been resolved from a legacy URL - redirect the user to the new URL and display a toast. - const path = `rules/id/${rule.id}${window.location.search}${window.location.hash}`; - spacesApi.ui.redirectLegacyUrl({ - path, - aliasPurpose: rule.alias_purpose, - objectNoun: i18nTranslate.translate( - 'xpack.triggersActionsUI.sections.ruleDetails.redirectObjectNoun', - { defaultMessage: 'rule' } - ), - }); - } - } - }, [rule, spacesApi]); - - const getLegacyUrlConflictCallout = useMemo(() => { - if (rule?.alias_target_id != null && spacesApi && rule.outcome === 'conflict') { - const aliasTargetId = rule.alias_target_id; - // We have resolved to one rule, but there is another one with a legacy URL associated with this page. Display a - // callout with a warning for the user, and provide a way for them to navigate to the other rule. - const otherRulePath = `rules/id/${aliasTargetId}${window.location.search}${window.location.hash}`; - return ( - <> - - {spacesApi.ui.components.getLegacyUrlConflict({ - objectNoun: i18nTranslate.translate( - 'xpack.triggersActionsUI.sections.ruleDetails.redirectObjectNoun', - { - defaultMessage: 'rule', - } - ), - currentObjectId: rule.id, - otherObjectId: aliasTargetId, - otherObjectPath: otherRulePath, - })} - - ); - } - return null; - }, [rule, spacesApi]); - - const ruleExecutionSettings = useRuleExecutionSettings(); - - useEffect(() => { - const hiddenTabs = []; - - if (!hasIndexRead) { - hiddenTabs.push(RuleDetailTabs.alerts); - } - if (!ruleExecutionSettings.extendedLogging.isEnabled) { - hiddenTabs.push(RuleDetailTabs.executionEvents); - } - if (rule != null) { - const hasEndpointList = (rule.exceptions_list ?? []).some( - (list) => list.type === ExceptionListTypeEnum.ENDPOINT - ); - if (!hasEndpointList) { - hiddenTabs.push(RuleDetailTabs.endpointExceptions); - } - } - - const tabs = omit>(hiddenTabs, ruleDetailTabs); - - setTabs(tabs); - }, [hasIndexRead, rule, ruleDetailTabs, ruleExecutionSettings]); + useLegacyUrlRedirect({ rule, spacesApi }); const showUpdating = useMemo( () => isLoadingIndexPattern || isAlertsLoading || loading, @@ -769,7 +652,7 @@ const RuleDetailsPageComponent: React.FC = ({ {ruleError} - {getLegacyUrlConflictCallout} + diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/legacy_url_conflict_callout.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/legacy_url_conflict_callout.test.tsx new file mode 100644 index 0000000000000..240bb988756f3 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/legacy_url_conflict_callout.test.tsx @@ -0,0 +1,113 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +// eslint-disable-next-line no-restricted-imports +import { LegacyUrlConflictCallOut } from './legacy_url_conflict_callout'; +import { render, screen } from '@testing-library/react'; +import type { Rule } from '../../../rule_management/logic'; + +const mockRedirectLegacyUrl = jest.fn(); +const mockGetLegacyUrlConflict = jest.fn(); + +const mockSpacesApi = { + getActiveSpace$: jest.fn(), + getActiveSpace: jest.fn(), + ui: { + components: { + getSpacesContextProvider: jest.fn(), + getShareToSpaceFlyout: jest.fn(), + getCopyToSpaceFlyout: jest.fn(), + getSpaceList: jest.fn(), + getEmbeddableLegacyUrlConflict: jest.fn(), + getSpaceAvatar: jest.fn(), + getLegacyUrlConflict: mockGetLegacyUrlConflict, + }, + redirectLegacyUrl: mockRedirectLegacyUrl, + useSpaces: jest.fn(), + }, +}; + +describe('', () => { + beforeEach(() => { + mockRedirectLegacyUrl.mockReset(); + mockGetLegacyUrlConflict.mockReset(); + }); + + it('renders null if no rule', () => { + render(); + expect(screen.queryByTestId('legacyUrlConflictCallOut-wrapper')).toBeNull(); + expect(mockRedirectLegacyUrl).not.toHaveBeenCalled(); + expect(mockGetLegacyUrlConflict).not.toHaveBeenCalled(); + }); + + it('renders null if spacesApi is undefined', () => { + render(); + expect(screen.queryByTestId('legacyUrlConflictCallOut-wrapper')).toBeNull(); + expect(mockRedirectLegacyUrl).not.toHaveBeenCalled(); + expect(mockGetLegacyUrlConflict).not.toHaveBeenCalled(); + }); + + it('renders null if rule.outcome is not "conflict"', () => { + render(); + expect(screen.queryByTestId('legacyUrlConflictCallOut-wrapper')).toBeNull(); + expect(mockRedirectLegacyUrl).not.toHaveBeenCalled(); + expect(mockGetLegacyUrlConflict).not.toHaveBeenCalled(); + }); + + it('renders LegacyUrlConfictCallout if rule.outcome is "conflict" and alias_target_id and spacesApi are defined', () => { + render( + + ); + expect(screen.queryByTestId('legacyUrlConflictCallOut-wrapper')).toBeInTheDocument(); + expect(mockRedirectLegacyUrl).not.toHaveBeenCalled(); + expect(mockGetLegacyUrlConflict).toHaveBeenCalledWith({ + currentObjectId: mockRule.id, + objectNoun: 'rule', + otherObjectId: 'mock_alias_target_id', + otherObjectPath: 'rules/id/mock_alias_target_id', + }); + }); +}); + +const mockRule: Rule = { + id: 'myfakeruleid', + author: [], + severity_mapping: [], + risk_score_mapping: [], + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + name: 'some-name', + severity: 'low', + type: 'query', + query: 'some query', + index: ['index-1'], + interval: '5m', + references: [], + actions: [], + enabled: false, + false_positives: [], + max_signals: 100, + tags: [], + threat: [], + throttle: null, + version: 1, + exceptions_list: [], + created_at: '2020-04-09T09:43:51.778Z', + created_by: 'elastic', + immutable: false, + updated_at: '2020-04-09T09:43:51.778Z', + updated_by: 'elastic', + related_integrations: [], + required_fields: [], + setup: '', +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/legacy_url_conflict_callout.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/legacy_url_conflict_callout.tsx new file mode 100644 index 0000000000000..bd8589a39e392 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/legacy_url_conflict_callout.tsx @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { EuiSpacer } from '@elastic/eui'; +import { i18n as i18nTranslate } from '@kbn/i18n'; +import type { SpacesApi } from '@kbn/spaces-plugin/public'; +import type { Rule } from '../../../rule_management/logic'; + +interface LegacyUrlConflictCallOutProps { + rule: Rule | null; + spacesApi?: SpacesApi; +} + +export const LegacyUrlConflictCallOut = React.memo( + ({ rule, spacesApi }) => { + if (rule?.alias_target_id != null && spacesApi && rule.outcome === 'conflict') { + const aliasTargetId = rule.alias_target_id; + // We have resolved to one rule, but there is another one with a legacy URL associated with this page. Display a + // callout with a warning for the user, and provide a way for them to navigate to the other rule. + const otherRulePath = `rules/id/${aliasTargetId}${window.location.search}${window.location.hash}`; + return ( +
    + + {spacesApi.ui.components.getLegacyUrlConflict({ + objectNoun: i18nTranslate.translate( + 'xpack.triggersActionsUI.sections.ruleDetails.redirectObjectNoun', + { + defaultMessage: 'rule', + } + ), + currentObjectId: rule.id, + otherObjectId: aliasTargetId, + otherObjectPath: otherRulePath, + })} +
    + ); + } + return null; + } +); + +LegacyUrlConflictCallOut.displayName = 'LegacyUrlConflictCallOut'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/rule_details_context.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/rule_details_context.tsx index 6ef168bcd8080..e08e32662cca6 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/rule_details_context.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/rule_details_context.tsx @@ -15,7 +15,7 @@ import type { } from '../../../../../common/api/detection_engine/rule_monitoring'; import { invariant } from '../../../../../common/utils/invariant'; import { useKibana } from '../../../../common/lib/kibana'; -import { RuleDetailTabs } from '.'; +import { RuleDetailTabs } from './use_rule_details_tabs'; export interface ExecutionLogTableState { /** diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/use_redirect_legacy_url.test.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/use_redirect_legacy_url.test.ts new file mode 100644 index 0000000000000..454904a251bf4 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/use_redirect_legacy_url.test.ts @@ -0,0 +1,115 @@ +/* + * 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. + */ +/* eslint-disable no-restricted-imports */ + +import { renderHook, cleanup } from '@testing-library/react-hooks'; +import type { UseLegacyUrlRedirectParams } from './use_redirect_legacy_url'; +import { useLegacyUrlRedirect } from './use_redirect_legacy_url'; +import type { Rule } from '../../../rule_management/logic'; + +const mockRedirectLegacyUrl = jest.fn(); +const mockGetLegacyUrlConflict = jest.fn(); + +const mockSpacesApi = { + getActiveSpace$: jest.fn(), + getActiveSpace: jest.fn(), + ui: { + components: { + getSpacesContextProvider: jest.fn(), + getShareToSpaceFlyout: jest.fn(), + getCopyToSpaceFlyout: jest.fn(), + getSpaceList: jest.fn(), + getEmbeddableLegacyUrlConflict: jest.fn(), + getSpaceAvatar: jest.fn(), + getLegacyUrlConflict: mockGetLegacyUrlConflict, + }, + redirectLegacyUrl: mockRedirectLegacyUrl, + useSpaces: jest.fn(), + }, +}; + +describe('useLegacyUrlRedirect', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(async () => { + cleanup(); + }); + + const render = (props: UseLegacyUrlRedirectParams) => + renderHook(() => useLegacyUrlRedirect({ ...props })); + + it('should not redirect if rule is null', () => { + render({ rule: null, spacesApi: mockSpacesApi }); + expect(mockRedirectLegacyUrl).not.toHaveBeenCalled(); + }); + + it('should not redirect if spacesApi is undefined', () => { + render({ + rule: { ...mockRule, id: '123', outcome: 'aliasMatch', alias_purpose: 'savedObjectImport' }, + spacesApi: undefined, + }); + expect(mockRedirectLegacyUrl).not.toHaveBeenCalled(); + }); + + it('should not redirect if outcome is not aliasMatch', () => { + render({ + rule: { ...mockRule, id: '123', outcome: 'exactMatch', alias_purpose: 'savedObjectImport' }, + spacesApi: mockSpacesApi, + }); + expect(mockRedirectLegacyUrl).not.toHaveBeenCalled(); + }); + + it('should redirect if rule is not null and outcome is aliasMatch', () => { + render({ + rule: { ...mockRule, id: '123', outcome: 'aliasMatch', alias_purpose: 'savedObjectImport' }, + spacesApi: mockSpacesApi, + }); + expect(mockRedirectLegacyUrl).toHaveBeenCalledWith({ + aliasPurpose: 'savedObjectImport', + objectNoun: 'rule', + path: 'rules/id/123', + }); + }); +}); + +const mockRule: Rule = { + id: 'myfakeruleid', + author: [], + severity_mapping: [], + risk_score_mapping: [], + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + name: 'some-name', + severity: 'low', + type: 'query', + query: 'some query', + index: ['index-1'], + interval: '5m', + references: [], + actions: [], + enabled: false, + false_positives: [], + max_signals: 100, + tags: [], + threat: [], + throttle: null, + version: 1, + exceptions_list: [], + created_at: '2020-04-09T09:43:51.778Z', + created_by: 'elastic', + immutable: false, + updated_at: '2020-04-09T09:43:51.778Z', + updated_by: 'elastic', + related_integrations: [], + required_fields: [], + setup: '', +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/use_redirect_legacy_url.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/use_redirect_legacy_url.ts new file mode 100644 index 0000000000000..10e442c0041fc --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/use_redirect_legacy_url.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useEffect } from 'react'; +import { i18n as i18nTranslate } from '@kbn/i18n'; +import type { SpacesApi } from '@kbn/spaces-plugin/public'; +import type { Rule } from '../../../rule_management/logic'; + +export interface UseLegacyUrlRedirectParams { + rule: Rule | null; + spacesApi?: SpacesApi; +} + +export const useLegacyUrlRedirect = ({ rule, spacesApi }: UseLegacyUrlRedirectParams) => { + useEffect(() => { + if (rule) { + const outcome = rule.outcome; + if (spacesApi && outcome === 'aliasMatch') { + // This rule has been resolved from a legacy URL - redirect the user to the new URL and display a toast. + const path = `rules/id/${rule.id}${window.location.search}${window.location.hash}`; + spacesApi.ui.redirectLegacyUrl({ + path, + aliasPurpose: rule.alias_purpose, + objectNoun: i18nTranslate.translate( + 'xpack.triggersActionsUI.sections.ruleDetails.redirectObjectNoun', + { defaultMessage: 'rule' } + ), + }); + } + } + }, [rule, spacesApi]); +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/use_rule_details_tabs.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/use_rule_details_tabs.test.tsx new file mode 100644 index 0000000000000..496fb0d94c3fc --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/use_rule_details_tabs.test.tsx @@ -0,0 +1,151 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook, cleanup } from '@testing-library/react-hooks'; +import type { UseRuleDetailsTabsProps } from './use_rule_details_tabs'; +import { RuleDetailTabs, useRuleDetailsTabs } from './use_rule_details_tabs'; +import type { Rule } from '../../../rule_management/logic'; + +import { useRuleExecutionSettings } from '../../../rule_monitoring'; +jest.mock('../../../rule_monitoring'); + +const mockRule: Rule = { + id: 'myfakeruleid', + author: [], + severity_mapping: [], + risk_score_mapping: [], + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + name: 'some-name', + severity: 'low', + type: 'query', + query: 'some query', + index: ['index-1'], + interval: '5m', + references: [], + actions: [], + enabled: false, + false_positives: [], + max_signals: 100, + tags: [], + threat: [], + throttle: null, + version: 1, + exceptions_list: [], + created_at: '2020-04-09T09:43:51.778Z', + created_by: 'elastic', + immutable: false, + updated_at: '2020-04-09T09:43:51.778Z', + updated_by: 'elastic', + related_integrations: [], + required_fields: [], + setup: '', +}; + +describe('useRuleDetailsTabs', () => { + beforeAll(() => { + (useRuleExecutionSettings as jest.Mock).mockReturnValue({ + extendedLogging: { + isEnabled: false, + minLevel: 'debug', + }, + }); + }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(async () => { + cleanup(); + }); + + const render = (props: UseRuleDetailsTabsProps) => + renderHook(() => useRuleDetailsTabs({ ...props })); + + it('does not return the alerts tab if the user does not have read permissions', async () => { + const tabs = render({ + rule: mockRule, + ruleId: mockRule.rule_id, + isExistingRule: true, + hasIndexRead: false, + }); + const tabsNames = Object.keys(tabs.result.current); + + expect(tabsNames).not.toContain(RuleDetailTabs.alerts); + }); + + it('always returns ths rule exception tab ', async () => { + const tabs = render({ + rule: mockRule, + ruleId: mockRule.rule_id, + isExistingRule: true, + hasIndexRead: true, + }); + const tabsNames = Object.keys(tabs.result.current); + + expect(tabsNames).toContain(RuleDetailTabs.exceptions); + }); + + it('renders endpoint exceptions tab when rule includes endpoint list', async () => { + const tabs = render({ + rule: { + ...mockRule, + outcome: 'conflict', + alias_target_id: 'aliased_rule_id', + alias_purpose: 'savedObjectConversion', + exceptions_list: [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + type: 'endpoint', + namespace_type: 'agnostic', + }, + ], + }, + ruleId: mockRule.rule_id, + isExistingRule: true, + hasIndexRead: true, + }); + const tabsNames = Object.keys(tabs.result.current); + + expect(tabsNames).toContain(RuleDetailTabs.endpointExceptions); + }); + + it('does not return the execution events tab if extended logging is disabled', async () => { + const tabs = render({ + rule: mockRule, + ruleId: mockRule.rule_id, + isExistingRule: true, + hasIndexRead: true, + }); + const tabsNames = Object.keys(tabs.result.current); + + expect(tabsNames).not.toContain(RuleDetailTabs.executionEvents); + }); + + it('returns the execution events tab if extended logging is enabled', async () => { + (useRuleExecutionSettings as jest.Mock).mockReturnValue({ + extendedLogging: { + isEnabled: true, + minLevel: 'debug', + }, + }); + const tabs = render({ + rule: mockRule, + ruleId: mockRule.rule_id, + isExistingRule: true, + hasIndexRead: true, + }); + const tabsNames = Object.keys(tabs.result.current); + + expect(tabsNames).toContain(RuleDetailTabs.executionEvents); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/use_rule_details_tabs.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/use_rule_details_tabs.tsx new file mode 100644 index 0000000000000..42a42caeb732d --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/use_rule_details_tabs.tsx @@ -0,0 +1,110 @@ +/* + * 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 { useEffect, useMemo, useState } from 'react'; +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import { omit } from 'lodash/fp'; +import * as detectionI18n from '../../../../detections/pages/detection_engine/translations'; +import * as i18n from './translations'; +import type { Rule } from '../../../rule_management/logic'; +import type { NavTab } from '../../../../common/components/navigation/types'; +import { useRuleExecutionSettings } from '../../../rule_monitoring'; + +export enum RuleDetailTabs { + alerts = 'alerts', + exceptions = 'rule_exceptions', + endpointExceptions = 'endpoint_exceptions', + executionResults = 'execution_results', + executionEvents = 'execution_events', +} + +export const RULE_DETAILS_TAB_NAME: Record = { + [RuleDetailTabs.alerts]: detectionI18n.ALERT, + [RuleDetailTabs.exceptions]: i18n.EXCEPTIONS_TAB, + [RuleDetailTabs.endpointExceptions]: i18n.ENDPOINT_EXCEPTIONS_TAB, + [RuleDetailTabs.executionResults]: i18n.EXECUTION_RESULTS_TAB, + [RuleDetailTabs.executionEvents]: i18n.EXECUTION_EVENTS_TAB, +}; + +export interface UseRuleDetailsTabsProps { + rule: Rule | null; + ruleId: string; + isExistingRule: boolean; + hasIndexRead: boolean | null; +} + +export const useRuleDetailsTabs = ({ + rule, + ruleId, + isExistingRule, + hasIndexRead, +}: UseRuleDetailsTabsProps) => { + const ruleDetailTabs = useMemo( + (): Record => ({ + [RuleDetailTabs.alerts]: { + id: RuleDetailTabs.alerts, + name: RULE_DETAILS_TAB_NAME[RuleDetailTabs.alerts], + disabled: false, + href: `/rules/id/${ruleId}/${RuleDetailTabs.alerts}`, + }, + [RuleDetailTabs.exceptions]: { + id: RuleDetailTabs.exceptions, + name: RULE_DETAILS_TAB_NAME[RuleDetailTabs.exceptions], + disabled: rule == null, + href: `/rules/id/${ruleId}/${RuleDetailTabs.exceptions}`, + }, + [RuleDetailTabs.endpointExceptions]: { + id: RuleDetailTabs.endpointExceptions, + name: RULE_DETAILS_TAB_NAME[RuleDetailTabs.endpointExceptions], + disabled: rule == null, + href: `/rules/id/${ruleId}/${RuleDetailTabs.endpointExceptions}`, + }, + [RuleDetailTabs.executionResults]: { + id: RuleDetailTabs.executionResults, + name: RULE_DETAILS_TAB_NAME[RuleDetailTabs.executionResults], + disabled: !isExistingRule, + href: `/rules/id/${ruleId}/${RuleDetailTabs.executionResults}`, + }, + [RuleDetailTabs.executionEvents]: { + id: RuleDetailTabs.executionEvents, + name: RULE_DETAILS_TAB_NAME[RuleDetailTabs.executionEvents], + disabled: !isExistingRule, + href: `/rules/id/${ruleId}/${RuleDetailTabs.executionEvents}`, + }, + }), + [isExistingRule, rule, ruleId] + ); + + const [pageTabs, setTabs] = useState>>(ruleDetailTabs); + + const ruleExecutionSettings = useRuleExecutionSettings(); + + useEffect(() => { + const hiddenTabs = []; + + if (!hasIndexRead) { + hiddenTabs.push(RuleDetailTabs.alerts); + } + if (!ruleExecutionSettings.extendedLogging.isEnabled) { + hiddenTabs.push(RuleDetailTabs.executionEvents); + } + if (rule != null) { + const hasEndpointList = (rule.exceptions_list ?? []).some( + (list) => list.type === ExceptionListTypeEnum.ENDPOINT + ); + if (!hasEndpointList) { + hiddenTabs.push(RuleDetailTabs.endpointExceptions); + } + } + + const tabs = omit>(hiddenTabs, ruleDetailTabs); + + setTabs(tabs); + }, [hasIndexRead, rule, ruleDetailTabs, ruleExecutionSettings]); + + return pageTabs; +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.test.tsx index 6150cb3b6d0e8..573419676a086 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.test.tsx @@ -671,7 +671,7 @@ describe('When the add exception modal is opened', () => { rules={[ { ...getRulesSchemaMock(), - investigation_fields: ['foo.bar'], + investigation_fields: { field_names: ['foo.bar'] }, exceptions_list: [], } as Rule, ]} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.tsx index d7081f195fefc..15880fcd423fc 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.tsx @@ -348,7 +348,7 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({ exceptionItemName, // With "rule_default" type, there is only ever one rule associated. // That is why it's ok to pull just the first item from rules array here. - ruleCustomHighlightedFields: rules?.[0]?.investigation_fields ?? [], + ruleCustomHighlightedFields: rules?.[0]?.investigation_fields?.field_names ?? [], }); if (populatedException) { setComment(i18n.ADD_RULE_EXCEPTION_FROM_ALERT_COMMENT(alertData._id)); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/meta.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/meta.tsx index 44109280fac9a..a324c19ff994d 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/meta.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/meta.tsx @@ -27,7 +27,7 @@ import { FormattedDate } from '../../../../common/components/formatted_date'; import { SecurityPageName } from '../../../../../common/constants'; import type { ExceptionListRuleReferencesSchema } from '../../../../../common/api/detection_engine/rule_exceptions'; import { SecuritySolutionLinkAnchor } from '../../../../common/components/links'; -import { RuleDetailTabs } from '../../../rule_details_ui/pages/rule_details'; +import { RuleDetailTabs } from '../../../rule_details_ui/pages/rule_details/use_rule_details_tabs'; import { getRuleDetailsTabUrl } from '../../../../common/components/link_to/redirect_to_detection_engine'; const StyledFlexItem = styled(EuiFlexItem)` diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/utils.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/utils.tsx index b84f8d8a8ff1c..712da253477ab 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/utils.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/utils.tsx @@ -33,7 +33,7 @@ import { } from '../../utils/helpers'; import { SecuritySolutionLinkAnchor } from '../../../../common/components/links'; import { getRuleDetailsTabUrl } from '../../../../common/components/link_to/redirect_to_detection_engine'; -import { RuleDetailTabs } from '../../../rule_details_ui/pages/rule_details'; +import { RuleDetailTabs } from '../../../rule_details_ui/pages/rule_details/use_rule_details_tabs'; import type { ExceptionListRuleReferencesInfoSchema, ExceptionListRuleReferencesSchema, diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_about_section.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_about_section.tsx index 12ca5b5683f68..5c3002946baf6 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_about_section.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_about_section.tsx @@ -283,10 +283,12 @@ const prepareAboutSectionListItems = (rule: RuleResponse): EuiDescriptionListPro }); } - if (rule.investigation_fields && rule.investigation_fields.length > 0) { + if (rule.investigation_fields && rule.investigation_fields.field_names.length > 0) { aboutSectionListItems.push({ title: i18n.INVESTIGATION_FIELDS_FIELD_LABEL, - description: , + description: ( + + ), }); } diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_definition_section.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_definition_section.tsx index 8957ebe1ff9d5..de9e257c798bc 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_definition_section.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_definition_section.tsx @@ -42,6 +42,7 @@ import { DataSourceType } from '../../../../detections/pages/detection_engine/ru import { convertHistoryStartToSize } from '../../../../detections/pages/detection_engine/rules/helpers'; import { MlJobLink } from '../../../../detections/components/rules/ml_job_link/ml_job_link'; import { useSecurityJobs } from '../../../../common/components/ml_popover/hooks/use_security_jobs'; +import { useKibana } from '../../../../common/lib/kibana/kibana_react'; import { BadgeList } from './badge_list'; import * as i18n from './translations'; @@ -107,11 +108,42 @@ interface IndexProps { const Index = ({ index }: IndexProps) => ; -interface DataViewProps { +interface DataViewIdProps { dataViewId: string; } -const DataView = ({ dataViewId }: DataViewProps) => {dataViewId}; +const DataViewId = ({ dataViewId }: DataViewIdProps) => {dataViewId}; + +interface DataViewIndexPatternProps { + dataViewId: string; +} + +const DataViewIndexPattern = ({ dataViewId }: DataViewIndexPatternProps) => { + const { data } = useKibana().services; + const [indexPattern, setIndexPattern] = React.useState(''); + const [hasError, setHasError] = React.useState(false); + + React.useEffect(() => { + data.dataViews + .get(dataViewId) + .then((dataView) => { + setIndexPattern(dataView.getIndexPattern()); + }) + .catch(() => { + setHasError(true); + }); + }, [data, dataViewId]); + + if (hasError) { + return {i18n.DATA_VIEW_INDEX_PATTERN_FETCH_ERROR_MESSAGE}; + } + + if (!indexPattern) { + return ; + } + + return {indexPattern}; +}; interface ThresholdProps { threshold: ThresholdType; @@ -299,10 +331,16 @@ const prepareDefinitionSectionListItems = ( } if ('data_view_id' in rule && rule.data_view_id) { - definitionSectionListItems.push({ - title: i18n.DATA_VIEW_FIELD_LABEL, - description: , - }); + definitionSectionListItems.push( + { + title: i18n.DATA_VIEW_ID_FIELD_LABEL, + description: , + }, + { + title: i18n.DATA_VIEW_INDEX_PATTERN_FIELD_LABEL, + description: , + } + ); } if (savedQuery) { diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/translations.ts index 55b0edc254d73..8e795b9477045 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/translations.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/translations.ts @@ -182,10 +182,24 @@ export const INDEX_FIELD_LABEL = i18n.translate( } ); -export const DATA_VIEW_FIELD_LABEL = i18n.translate( - 'xpack.securitySolution.detectionEngine.ruleDetails.dataViewFieldLabel', +export const DATA_VIEW_ID_FIELD_LABEL = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDetails.dataViewIdFieldLabel', { - defaultMessage: 'Data View', + defaultMessage: 'Data view', + } +); + +export const DATA_VIEW_INDEX_PATTERN_FIELD_LABEL = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDetails.dataViewIndexPatternFieldLabel', + { + defaultMessage: 'Data view index pattern', + } +); + +export const DATA_VIEW_INDEX_PATTERN_FETCH_ERROR_MESSAGE = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDetails.dataViewIndexPatternFetchErrorMessage', + { + defaultMessage: 'Could not load data view index pattern', } ); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/coverage_overview/build_coverage_overview_mitre_graph.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/coverage_overview/build_coverage_overview_mitre_graph.ts index 7a775b3292991..acae46d0f3701 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/coverage_overview/build_coverage_overview_mitre_graph.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/coverage_overview/build_coverage_overview_mitre_graph.ts @@ -15,6 +15,24 @@ import type { CoverageOverviewMitreSubTechnique } from '../../model/coverage_ove import type { CoverageOverviewMitreTactic } from '../../model/coverage_overview/mitre_tactic'; import type { CoverageOverviewMitreTechnique } from '../../model/coverage_overview/mitre_technique'; +// The order the tactic columns will appear in on the coverage overview page +const tacticOrder = [ + 'TA0043', + 'TA0042', + 'TA0001', + 'TA0002', + 'TA0003', + 'TA0004', + 'TA0005', + 'TA0006', + 'TA0007', + 'TA0008', + 'TA0009', + 'TA0011', + 'TA0010', + 'TA0040', +]; + export function buildCoverageOverviewMitreGraph( tactics: MitreTactic[], techniques: MitreTechnique[], @@ -67,9 +85,13 @@ export function buildCoverageOverviewMitreGraph( } } + const sortedTactics = tactics.sort( + (a, b) => tacticOrder.indexOf(a.id) - tacticOrder.indexOf(b.id) + ); + const result: CoverageOverviewMitreTactic[] = []; - for (const tactic of tactics) { + for (const tactic of sortedTactics) { result.push({ id: tactic.id, name: tactic.name, diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/types.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/types.ts index 8c89032a7d15b..4bb86c26ca760 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/types.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/types.ts @@ -73,7 +73,7 @@ import { TimestampField, TimestampOverride, TimestampOverrideFallbackDisabled, - RuleCustomHighlightedFieldArray, + InvestigationFields, } from '../../../../common/api/detection_engine/model/rule_schema'; import type { @@ -202,7 +202,7 @@ export const RuleSchema = t.intersection([ version: RuleVersion, execution_summary: RuleExecutionSummary, alert_suppression: AlertSuppression, - investigation_fields: RuleCustomHighlightedFieldArray, + investigation_fields: InvestigationFields, }), ]); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/model/coverage_overview/mitre_technique.test.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/model/coverage_overview/mitre_technique.test.ts new file mode 100644 index 0000000000000..01794b34c8ee7 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/model/coverage_overview/mitre_technique.test.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CoverageOverviewRuleActivity } from '../../../../../common/api/detection_engine'; +import { getTotalRuleCount } from './mitre_technique'; +import { getMockCoverageOverviewMitreTechnique } from './__mocks__'; + +describe('getTotalRuleCount', () => { + it('returns count of all rules when no activity filter is present', () => { + const payload = getMockCoverageOverviewMitreTechnique(); + expect(getTotalRuleCount(payload)).toEqual(2); + }); + + it('returns count of one rule type when an activity filter is present', () => { + const payload = getMockCoverageOverviewMitreTechnique(); + expect(getTotalRuleCount(payload, [CoverageOverviewRuleActivity.Disabled])).toEqual(1); + }); + + it('returns count of multiple rule type when multiple activity filter is present', () => { + const payload = getMockCoverageOverviewMitreTechnique(); + expect( + getTotalRuleCount(payload, [ + CoverageOverviewRuleActivity.Enabled, + CoverageOverviewRuleActivity.Disabled, + ]) + ).toEqual(2); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/model/coverage_overview/mitre_technique.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/model/coverage_overview/mitre_technique.ts index 35587ca59fdda..589629d643810 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/model/coverage_overview/mitre_technique.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/model/coverage_overview/mitre_technique.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { CoverageOverviewRuleActivity } from '../../../../../common/api/detection_engine'; import type { CoverageOverviewMitreSubTechnique } from './mitre_subtechnique'; import type { CoverageOverviewRule } from './rule'; @@ -20,3 +21,20 @@ export interface CoverageOverviewMitreTechnique { disabledRules: CoverageOverviewRule[]; availableRules: CoverageOverviewRule[]; } + +export const getTotalRuleCount = ( + technique: CoverageOverviewMitreTechnique, + activity?: CoverageOverviewRuleActivity[] +): number => { + if (!activity) { + return technique.enabledRules.length + technique.disabledRules.length; + } + let totalRuleCount = 0; + if (activity.includes(CoverageOverviewRuleActivity.Enabled)) { + totalRuleCount += technique.enabledRules.length; + } + if (activity.includes(CoverageOverviewRuleActivity.Disabled)) { + totalRuleCount += technique.disabledRules.length; + } + return totalRuleCount; +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/ml_rule_warning_popover/ml_rule_warning_popover.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/ml_rule_warning_popover/ml_rule_warning_popover.tsx index 3575a030def5a..493686f9ceb19 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/ml_rule_warning_popover/ml_rule_warning_popover.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/ml_rule_warning_popover/ml_rule_warning_popover.tsx @@ -27,7 +27,7 @@ import { isMlRule } from '../../../../../common/detection_engine/utils'; import { getCapitalizedStatusText } from '../../../../detections/components/rules/rule_execution_status/utils'; import type { Rule } from '../../../rule_management/logic'; import { isJobStarted } from '../../../../../common/machine_learning/helpers'; -import { RuleDetailTabs } from '../../../rule_details_ui/pages/rule_details'; +import { RuleDetailTabs } from '../../../rule_details_ui/pages/rule_details/use_rule_details_tabs'; const POPOVER_WIDTH = '340px'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/ml_rule_warning_popover.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/ml_rule_warning_popover.tsx index 3be3cc03061b4..3d9e1f05c6e1e 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/ml_rule_warning_popover.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/ml_rule_warning_popover.tsx @@ -27,7 +27,7 @@ import { isMlRule } from '../../../../../common/detection_engine/utils'; import { getCapitalizedStatusText } from '../../../../detections/components/rules/rule_execution_status/utils'; import type { Rule } from '../../../rule_management/logic'; import { isJobStarted } from '../../../../../common/machine_learning/helpers'; -import { RuleDetailTabs } from '../../../rule_details_ui/pages/rule_details'; +import { RuleDetailTabs } from '../../../rule_details_ui/pages/rule_details/use_rule_details_tabs'; const POPOVER_WIDTH = '340px'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table_toolbar.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table_toolbar.tsx index c1aec33b2a2f7..fb2c17ddb2fa1 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table_toolbar.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table_toolbar.tsx @@ -5,12 +5,18 @@ * 2.0. */ -import React, { useMemo } from 'react'; +import React, { useCallback, useMemo } from 'react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { NewChat } from '@kbn/elastic-assistant'; import { useUserData } from '../../../../detections/components/user_info'; import { TabNavigation } from '../../../../common/components/navigation/tab_navigation'; import { usePrebuiltRulesStatus } from '../../../rule_management/logic/prebuilt_rules/use_prebuilt_rules_status'; import { useRuleManagementFilters } from '../../../rule_management/logic/use_rule_management_filters'; import * as i18n from './translations'; +import { getPromptContextFromDetectionRules } from '../../../../assistant/helpers'; +import { useRulesTableContext } from './rules_table/rules_table_context'; +import { useAssistantAvailability } from '../../../../assistant/use_assistant_availability'; +import * as i18nAssistant from '../../../../detections/pages/detection_engine/rules/translations'; export enum AllRulesTabs { management = 'management', @@ -71,7 +77,39 @@ export const RulesTableToolbar = React.memo(() => { [installedTotal, updateTotal, shouldDisplayRuleUpdatesTab] ); - return ; + // Assistant integration for using selected rules as prompt context + const { hasAssistantPrivilege } = useAssistantAvailability(); + const { + state: { rules, selectedRuleIds }, + } = useRulesTableContext(); + const selectedRules = useMemo( + () => rules.filter((rule) => selectedRuleIds.includes(rule.id)), + [rules, selectedRuleIds] + ); + const getPromptContext = useCallback( + async () => getPromptContextFromDetectionRules(selectedRules), + [selectedRules] + ); + + return ( + + + + + + {hasAssistantPrivilege && selectedRules.length > 0 && ( + + )} + + + ); }); RulesTableToolbar.displayName = 'RulesTableToolbar'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/use_columns.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/use_columns.tsx index 5629ed865a565..cc975a62be4a0 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/use_columns.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/use_columns.tsx @@ -37,7 +37,7 @@ import { RuleStatusBadge } from '../../../../detections/components/rules/rule_ex import { RuleSwitch } from '../../../../detections/components/rules/rule_switch'; import { SeverityBadge } from '../../../../detections/components/rules/severity_badge'; import * as i18n from '../../../../detections/pages/detection_engine/rules/translations'; -import { RuleDetailTabs } from '../../../rule_details_ui/pages/rule_details'; +import { RuleDetailTabs } from '../../../rule_details_ui/pages/rule_details/use_rule_details_tabs'; import type { Rule } from '../../../rule_management/logic'; import { PopoverTooltip } from './popover_tooltip'; import { useRulesTableContext } from './rules_table/rules_table_context'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/coverage_overview/constants.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/coverage_overview/constants.ts index 7c170579a217a..3bbc2f5dfbfb9 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/coverage_overview/constants.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/coverage_overview/constants.ts @@ -11,8 +11,6 @@ import { } from '../../../../../common/api/detection_engine'; import * as i18n from './translations'; -export const coverageOverviewPaletteColors = ['#00BFB326', '#00BFB34D', '#00BFB399', '#00BFB3']; - export const coverageOverviewPanelWidth = 160; export const coverageOverviewLegendWidth = 380; @@ -25,10 +23,10 @@ export const coverageOverviewFilterWidth = 300; * A corresponding color is applied if rules count >= a specific threshold */ export const coverageOverviewCardColorThresholds = [ - { threshold: 10, color: coverageOverviewPaletteColors[3] }, - { threshold: 7, color: coverageOverviewPaletteColors[2] }, - { threshold: 3, color: coverageOverviewPaletteColors[1] }, - { threshold: 1, color: coverageOverviewPaletteColors[0] }, + { threshold: 10, color: '#00BFB3' }, + { threshold: 7, color: '#00BFB399' }, + { threshold: 3, color: '#00BFB34D' }, + { threshold: 1, color: '#00BFB326' }, ]; export const ruleActivityFilterDefaultOptions = [ diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/coverage_overview/coverage_overview_dashboard.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/coverage_overview/coverage_overview_dashboard.tsx index 6762ee6b1b0e2..11ee8f0d70bbc 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/coverage_overview/coverage_overview_dashboard.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/coverage_overview/coverage_overview_dashboard.tsx @@ -5,7 +5,8 @@ * 2.0. */ import React from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiSpacer, EuiText } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText } from '@elastic/eui'; +import { CoverageOverviewLink } from '../../../../common/components/links_to_docs'; import { HeaderPage } from '../../../../common/components/header_page'; import * as i18n from './translations'; @@ -14,26 +15,27 @@ import { CoverageOverviewMitreTechniquePanelPopover } from './technique_panel_po import { CoverageOverviewFiltersPanel } from './filters_panel'; import { useCoverageOverviewDashboardContext } from './coverage_overview_dashboard_context'; +const CoverageOverviewHeaderComponent = () => ( + + {i18n.CoverageOverviewDashboardInformation} + + } + /> +); + +const CoverageOverviewHeader = React.memo(CoverageOverviewHeaderComponent); + const CoverageOverviewDashboardComponent = () => { const { state: { data }, } = useCoverageOverviewDashboardContext(); - const subtitle = ( - - {i18n.CoverageOverviewDashboardInformation}{' '} - - {i18n.CoverageOverviewDashboardInformationLink} - - - ); + return ( <> - + diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/coverage_overview/helpers.test.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/coverage_overview/helpers.test.ts index 738eb981f37bb..5a1aee424352a 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/coverage_overview/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/coverage_overview/helpers.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { CoverageOverviewRuleActivity } from '../../../../../common/api/detection_engine'; +import type { CoverageOverviewRuleActivity } from '../../../../../common/api/detection_engine'; import { getCoverageOverviewFilterMock } from '../../../../../common/api/detection_engine/rule_management/coverage_overview/coverage_overview_route.mock'; import { getMockCoverageOverviewMitreSubTechnique, @@ -17,7 +17,6 @@ import { extractSelected, getNumOfCoveredSubtechniques, getNumOfCoveredTechniques, - getTotalRuleCount, populateSelected, } from './helpers'; @@ -89,26 +88,4 @@ describe('helpers', () => { ]); }); }); - - describe('getTotalRuleCount', () => { - it('returns count of all rules when no activity filter is present', () => { - const payload = getMockCoverageOverviewMitreTechnique(); - expect(getTotalRuleCount(payload)).toEqual(2); - }); - - it('returns count of one rule type when an activity filter is present', () => { - const payload = getMockCoverageOverviewMitreTechnique(); - expect(getTotalRuleCount(payload, [CoverageOverviewRuleActivity.Disabled])).toEqual(1); - }); - - it('returns count of multiple rule type when multiple activity filter is present', () => { - const payload = getMockCoverageOverviewMitreTechnique(); - expect( - getTotalRuleCount(payload, [ - CoverageOverviewRuleActivity.Enabled, - CoverageOverviewRuleActivity.Disabled, - ]) - ).toEqual(2); - }); - }); }); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/coverage_overview/helpers.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/coverage_overview/helpers.ts index 7e8f757561a78..82d50e7b9721b 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/coverage_overview/helpers.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/coverage_overview/helpers.ts @@ -6,8 +6,10 @@ */ import type { EuiSelectableOption } from '@elastic/eui'; -import type { CoverageOverviewRuleSource } from '../../../../../common/api/detection_engine'; -import { CoverageOverviewRuleActivity } from '../../../../../common/api/detection_engine'; +import type { + CoverageOverviewRuleActivity, + CoverageOverviewRuleSource, +} from '../../../../../common/api/detection_engine'; import type { CoverageOverviewMitreTactic } from '../../../rule_management/model/coverage_overview/mitre_tactic'; import type { CoverageOverviewMitreTechnique } from '../../../rule_management/model/coverage_overview/mitre_technique'; import { coverageOverviewCardColorThresholds } from './constants'; @@ -41,20 +43,3 @@ export const populateSelected = ( allOptions.map((option) => selected.includes(option.label) ? { ...option, checked: 'on' } : option ); - -export const getTotalRuleCount = ( - technique: CoverageOverviewMitreTechnique, - activity?: CoverageOverviewRuleActivity[] -): number => { - if (!activity) { - return technique.enabledRules.length + technique.disabledRules.length; - } - let totalRuleCount = 0; - if (activity.includes(CoverageOverviewRuleActivity.Enabled)) { - totalRuleCount += technique.enabledRules.length; - } - if (activity.includes(CoverageOverviewRuleActivity.Disabled)) { - totalRuleCount += technique.disabledRules.length; - } - return totalRuleCount; -}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/coverage_overview/technique_panel.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/coverage_overview/technique_panel.tsx index e182b6445513c..821f0f18dcd72 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/coverage_overview/technique_panel.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/coverage_overview/technique_panel.tsx @@ -9,9 +9,10 @@ import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui'; import { css } from '@emotion/css'; import React, { memo, useCallback, useMemo } from 'react'; import type { CoverageOverviewMitreTechnique } from '../../../rule_management/model/coverage_overview/mitre_technique'; +import { getTotalRuleCount } from '../../../rule_management/model/coverage_overview/mitre_technique'; import { coverageOverviewPanelWidth } from './constants'; import { useCoverageOverviewDashboardContext } from './coverage_overview_dashboard_context'; -import { getCardBackgroundColor, getTotalRuleCount } from './helpers'; +import { getCardBackgroundColor } from './helpers'; import { CoverageOverviewPanelRuleStats } from './shared_components/panel_rule_stats'; import * as i18n from './translations'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/coverage_overview/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/coverage_overview/translations.ts index c3e205531fdce..eb9f06c350421 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/coverage_overview/translations.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/coverage_overview/translations.ts @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; export const COVERAGE_OVERVIEW_DASHBOARD_TITLE = i18n.translate( 'xpack.securitySolution.coverageOverviewDashboard.pageTitle', { - defaultMessage: 'MITRE ATT&CK\u00AE Coverage', + defaultMessage: 'MITRE ATT&CK\u00AE coverage', } ); @@ -174,13 +174,6 @@ export const CoverageOverviewDashboardInformation = i18n.translate( 'xpack.securitySolution.coverageOverviewDashboard.dashboardInformation', { defaultMessage: - 'The interactive MITRE ATT&CK coverage below shows the current state of your coverage from installed rules, click on a cell to view further details. Unmapped rules will not be displayed. View further information from our', - } -); - -export const CoverageOverviewDashboardInformationLink = i18n.translate( - 'xpack.securitySolution.coverageOverviewDashboard.dashboardInformationLink', - { - defaultMessage: 'docs.', + "Your current coverage of MITRE ATT&CK\u00AE tactics and techniques, based on installed rules. Click a cell to view and enable a technique's rules. Rules must be mapped to the MITRE ATT&CK\u00AE framework to be displayed.", } ); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/rule_management/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/rule_management/index.tsx index 2f5a202459f79..b27a322331fc7 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/rule_management/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/rule_management/index.tsx @@ -32,8 +32,8 @@ import { useInvalidateFindRulesQuery } from '../../../rule_management/api/hooks/ import { importRules } from '../../../rule_management/logic'; import { AllRules } from '../../components/rules_table'; import { RulesTableContextProvider } from '../../components/rules_table/rules_table/rules_table_context'; -import { SuperHeader } from './super_header'; import { useInvalidateFetchCoverageOverviewQuery } from '../../../rule_management/api/hooks/use_fetch_coverage_overview'; +import { HeaderPage } from '../../../../common/components/header_page'; const RulesPageComponent: React.FC = () => { const [isImportModalVisible, showImportModal, hideImportModal] = useBoolState(); @@ -110,7 +110,7 @@ const RulesPageComponent: React.FC = () => { - + @@ -149,7 +149,7 @@ const RulesPageComponent: React.FC = () => { - + diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/rule_management/super_header.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/rule_management/super_header.tsx deleted file mode 100644 index b3e7d6f3f6401..0000000000000 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/rule_management/super_header.tsx +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { NewChat } from '@kbn/elastic-assistant'; -import React, { useCallback, useMemo } from 'react'; - -import { useAssistantAvailability } from '../../../../assistant/use_assistant_availability'; -import { getPromptContextFromDetectionRules } from '../../../../assistant/helpers'; -import { HeaderPage } from '../../../../common/components/header_page'; -import { useRulesTableContext } from '../../components/rules_table/rules_table/rules_table_context'; -import * as i18n from '../../../../detections/pages/detection_engine/rules/translations'; - -export const SuperHeader: React.FC<{ children: React.ReactNode }> = React.memo(({ children }) => { - const { hasAssistantPrivilege } = useAssistantAvailability(); - const memoizedChildren = useMemo(() => children, [children]); - // Rules state - const { - state: { rules, selectedRuleIds }, - } = useRulesTableContext(); - - const selectedRules = useMemo( - () => rules.filter((rule) => selectedRuleIds.includes(rule.id)), - [rules, selectedRuleIds] - ); - - const getPromptContext = useCallback( - async () => getPromptContextFromDetectionRules(selectedRules), - [selectedRules] - ); - - return ( - - {i18n.PAGE_TITLE}{' '} - {hasAssistantPrivilege && selectedRules.length > 0 && ( - - {'🪄✨'} - - )} - - } - > - {memoizedChildren} - - ); -}); - -SuperHeader.displayName = 'NewChatComponent'; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/schema.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/schema.tsx index d996ee4e49592..1f90b75413672 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/schema.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/schema.tsx @@ -78,21 +78,11 @@ export const schema: FormSchema = { }, ], }, - dataViewTitle: { - label: i18n.translate( - 'xpack.securitySolution.detectionEngine.createRule.stepAboutRule.dataViewSelector', - { - defaultMessage: 'Data View', - } - ), - validations: [], - }, - // TODO: populate the dataViewTitle in a better way dataViewId: { label: i18n.translate( 'xpack.securitySolution.detectionEngine.createRule.stepAboutRule.dataViewSelector', { - defaultMessage: 'Data View', + defaultMessage: 'Data view', } ), fieldsToValidateOnChange: ['dataViewId'], @@ -128,6 +118,15 @@ export const schema: FormSchema = { }, ], }, + dataViewTitle: { + label: i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepAboutRule.dataViewTitleSelector', + { + defaultMessage: 'Data view index pattern', + } + ), + validations: [], + }, eqlOptions: {}, queryBar: { validations: [ diff --git a/x-pack/plugins/security_solution/public/detections/links.ts b/x-pack/plugins/security_solution/public/detections/links.ts index 79271592d174e..1228b21d3363c 100644 --- a/x-pack/plugins/security_solution/public/detections/links.ts +++ b/x-pack/plugins/security_solution/public/detections/links.ts @@ -14,7 +14,7 @@ export const links: LinkItem = { title: ALERTS, path: ALERTS_PATH, capabilities: [`${SERVER_APP_ID}.show`], - globalNavPosition: 2, + globalNavPosition: 3, globalSearchKeywords: [ i18n.translate('xpack.securitySolution.appLinks.alerts', { defaultMessage: 'Alerts', diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/breadcrumbs.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/breadcrumbs.ts index 0bd84ee4724ce..7b2cabd83f22e 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/breadcrumbs.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/breadcrumbs.ts @@ -6,6 +6,10 @@ */ import type { ChromeBreadcrumb } from '@kbn/core/public'; +import { + RuleDetailTabs, + RULE_DETAILS_TAB_NAME, +} from '../../../../detection_engine/rule_details_ui/pages/rule_details/use_rule_details_tabs'; import { getRuleDetailsTabUrl, getRuleDetailsUrl, @@ -14,10 +18,6 @@ import * as i18nRules from './translations'; import { SecurityPageName } from '../../../../app/types'; import { RULES_PATH } from '../../../../../common/constants'; import type { GetTrailingBreadcrumbs } from '../../../../common/components/navigation/breadcrumbs/types'; -import { - RuleDetailTabs, - RULE_DETAILS_TAB_NAME, -} from '../../../../detection_engine/rule_details_ui/pages/rule_details'; import { DELETED_RULE } from '../../../../detection_engine/rule_details_ui/pages/rule_details/translations'; const getRuleDetailsTabName = (tabName: string): string => { diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx index 78c81f5c9b1d5..5cdf1ed93ac5d 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx @@ -231,7 +231,7 @@ export const getAboutStepsData = (rule: Rule, detailsView: boolean): AboutStepRu isMappingChecked: riskScoreMapping.length > 0, }, falsePositives, - investigationFields: investigationFields ?? [], + investigationFields: investigationFields?.field_names ?? [], threat: threat as Threats, threatIndicatorPath, }; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts index c5b98b1bfd39d..0d03c43d3ada5 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts @@ -34,6 +34,7 @@ import type { SetupGuide, TimestampOverride, AlertSuppressionMissingFields, + InvestigationFields, } from '../../../../../common/api/detection_engine/model/rule_schema'; import type { SortOrder } from '../../../../../common/api/detection_engine'; import type { EqlOptionsSelected } from '../../../../../common/search_strategy'; @@ -239,7 +240,7 @@ export interface AboutStepRuleJson { timestamp_override?: TimestampOverride; timestamp_override_fallback_disabled?: boolean; note?: string; - investigation_fields?: string[]; + investigation_fields?: InvestigationFields; } export interface ScheduleStepRuleJson { diff --git a/x-pack/plugins/security_solution/public/exceptions/components/list_details_link_anchor/index.tsx b/x-pack/plugins/security_solution/public/exceptions/components/list_details_link_anchor/index.tsx index d1cb24e8d066b..4b54600fc454d 100644 --- a/x-pack/plugins/security_solution/public/exceptions/components/list_details_link_anchor/index.tsx +++ b/x-pack/plugins/security_solution/public/exceptions/components/list_details_link_anchor/index.tsx @@ -7,9 +7,9 @@ import React from 'react'; import type { FC } from 'react'; +import { RuleDetailTabs } from '../../../detection_engine/rule_details_ui/pages/rule_details/use_rule_details_tabs'; import { SecurityPageName } from '../../../../common/constants'; import { getRuleDetailsTabUrl } from '../../../common/components/link_to/redirect_to_detection_engine'; -import { RuleDetailTabs } from '../../../detection_engine/rule_details_ui/pages/rule_details'; import { SecuritySolutionLinkAnchor } from '../../../common/components/links'; interface LinkAnchorProps { diff --git a/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/translations.ts b/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/translations.ts index 55960cca0faf2..bf4002cd5568c 100644 --- a/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/translations.ts +++ b/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/translations.ts @@ -40,7 +40,7 @@ export const HOST_RISK_TOOLTIP = i18n.translate( 'xpack.securitySolution.hostsTable.hostRiskToolTip', { defaultMessage: - 'Host risk classification is determined by host risk score. Hosts classified as Critical or High are indicated as risky.', + "The host's risk score determines its risk classification. Risky hosts are labeled as critical or high.", } ); diff --git a/x-pack/plugins/security_solution/public/explore/links.ts b/x-pack/plugins/security_solution/public/explore/links.ts index a1da1528845ae..e0f97f813f5da 100644 --- a/x-pack/plugins/security_solution/public/explore/links.ts +++ b/x-pack/plugins/security_solution/public/explore/links.ts @@ -180,7 +180,7 @@ export const exploreLinks: LinkItem = { id: SecurityPageName.exploreLanding, title: EXPLORE, path: EXPLORE_PATH, - globalNavPosition: 7, + globalNavPosition: 8, capabilities: [`${SERVER_APP_ID}.show`], globalSearchKeywords: [ i18n.translate('xpack.securitySolution.appLinks.explore', { diff --git a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/dns/index.tsx b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/dns/index.tsx index 806c8d67e0c8e..cf52d2b4ee0eb 100644 --- a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/dns/index.tsx +++ b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/dns/index.tsx @@ -10,7 +10,7 @@ import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; import { Subscription } from 'rxjs'; -import { isCompleteResponse, isErrorResponse } from '@kbn/data-plugin/common'; +import { isCompleteResponse } from '@kbn/data-plugin/common'; import { useAppToasts } from '../../../../../common/hooks/use_app_toasts'; import type { inputsModel } from '../../../../../common/store'; import { createFilter } from '../../../../../common/containers/helpers'; @@ -69,7 +69,7 @@ export const useNetworkKpiDns = ({ isInspected: false, refetch: refetch.current, }); - const { addError, addWarning } = useAppToasts(); + const { addError } = useAppToasts(); const networkKpiDnsSearch = useCallback( (request: NetworkKpiDnsRequestOptions | null) => { @@ -97,10 +97,6 @@ export const useNetworkKpiDns = ({ refetch: refetch.current, })); searchSubscription$.current.unsubscribe(); - } else if (isErrorResponse(response)) { - setLoading(false); - addWarning(i18n.ERROR_NETWORK_KPI_DNS); - searchSubscription$.current.unsubscribe(); } }, error: (msg) => { @@ -117,7 +113,7 @@ export const useNetworkKpiDns = ({ asyncSearch(); refetch.current = asyncSearch; }, - [data.search, addError, addWarning, skip] + [data.search, addError, skip] ); useEffect(() => { diff --git a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/network_events/index.tsx b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/network_events/index.tsx index fe631b6b7e975..378120d155e16 100644 --- a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/network_events/index.tsx +++ b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/network_events/index.tsx @@ -10,7 +10,7 @@ import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; import { Subscription } from 'rxjs'; -import { isCompleteResponse, isErrorResponse } from '@kbn/data-plugin/common'; +import { isCompleteResponse } from '@kbn/data-plugin/common'; import { useAppToasts } from '../../../../../common/hooks/use_app_toasts'; import type { inputsModel } from '../../../../../common/store'; import { createFilter } from '../../../../../common/containers/helpers'; @@ -70,7 +70,7 @@ export const useNetworkKpiNetworkEvents = ({ isInspected: false, refetch: refetch.current, }); - const { addError, addWarning } = useAppToasts(); + const { addError } = useAppToasts(); const networkKpiNetworkEventsSearch = useCallback( (request: NetworkKpiNetworkEventsRequestOptions | null) => { @@ -101,10 +101,6 @@ export const useNetworkKpiNetworkEvents = ({ refetch: refetch.current, })); searchSubscription$.current.unsubscribe(); - } else if (isErrorResponse(response)) { - setLoading(false); - addWarning(i18n.ERROR_NETWORK_KPI_NETWORK_EVENTS); - searchSubscription$.current.unsubscribe(); } }, error: (msg) => { @@ -121,7 +117,7 @@ export const useNetworkKpiNetworkEvents = ({ asyncSearch(); refetch.current = asyncSearch; }, - [data.search, addError, addWarning, skip] + [data.search, addError, skip] ); useEffect(() => { diff --git a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/tls_handshakes/index.tsx b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/tls_handshakes/index.tsx index 63beacc2d297d..b53c07640220e 100644 --- a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/tls_handshakes/index.tsx +++ b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/tls_handshakes/index.tsx @@ -10,7 +10,7 @@ import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; import { Subscription } from 'rxjs'; -import { isCompleteResponse, isErrorResponse } from '@kbn/data-plugin/common'; +import { isCompleteResponse } from '@kbn/data-plugin/common'; import { useAppToasts } from '../../../../../common/hooks/use_app_toasts'; import type { inputsModel } from '../../../../../common/store'; import { createFilter } from '../../../../../common/containers/helpers'; @@ -70,7 +70,7 @@ export const useNetworkKpiTlsHandshakes = ({ isInspected: false, refetch: refetch.current, }); - const { addError, addWarning } = useAppToasts(); + const { addError } = useAppToasts(); const networkKpiTlsHandshakesSearch = useCallback( (request: NetworkKpiTlsHandshakesRequestOptions | null) => { @@ -100,10 +100,6 @@ export const useNetworkKpiTlsHandshakes = ({ refetch: refetch.current, })); searchSubscription$.current.unsubscribe(); - } else if (isErrorResponse(response)) { - setLoading(false); - addWarning(i18n.ERROR_NETWORK_KPI_TLS_HANDSHAKES); - searchSubscription$.current.unsubscribe(); } }, error: (msg) => { @@ -120,7 +116,7 @@ export const useNetworkKpiTlsHandshakes = ({ asyncSearch(); refetch.current = asyncSearch; }, - [data.search, addError, addWarning, skip] + [data.search, addError, skip] ); useEffect(() => { diff --git a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_flows/index.tsx b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_flows/index.tsx index 0d62d4e286201..6ff0eb5372a19 100644 --- a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_flows/index.tsx +++ b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_flows/index.tsx @@ -10,7 +10,7 @@ import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; import { Subscription } from 'rxjs'; -import { isCompleteResponse, isErrorResponse } from '@kbn/data-plugin/common'; +import { isCompleteResponse } from '@kbn/data-plugin/common'; import { useAppToasts } from '../../../../../common/hooks/use_app_toasts'; import type { inputsModel } from '../../../../../common/store'; import { createFilter } from '../../../../../common/containers/helpers'; @@ -70,7 +70,7 @@ export const useNetworkKpiUniqueFlows = ({ isInspected: false, refetch: refetch.current, }); - const { addError, addWarning } = useAppToasts(); + const { addError } = useAppToasts(); const networkKpiUniqueFlowsSearch = useCallback( (request: NetworkKpiUniqueFlowsRequestOptions | null) => { @@ -100,10 +100,6 @@ export const useNetworkKpiUniqueFlows = ({ refetch: refetch.current, })); searchSubscription$.current.unsubscribe(); - } else if (isErrorResponse(response)) { - setLoading(false); - addWarning(i18n.ERROR_NETWORK_KPI_UNIQUE_FLOWS); - searchSubscription$.current.unsubscribe(); } }, error: (msg) => { @@ -120,7 +116,7 @@ export const useNetworkKpiUniqueFlows = ({ asyncSearch(); refetch.current = asyncSearch; }, - [data.search, addError, addWarning, skip] + [data.search, addError, skip] ); useEffect(() => { diff --git a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_private_ips/index.tsx b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_private_ips/index.tsx index 2e73cb18ef52d..45435665dba41 100644 --- a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_private_ips/index.tsx +++ b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_private_ips/index.tsx @@ -10,7 +10,7 @@ import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; import { Subscription } from 'rxjs'; -import { isCompleteResponse, isErrorResponse } from '@kbn/data-plugin/common'; +import { isCompleteResponse } from '@kbn/data-plugin/common'; import { useAppToasts } from '../../../../../common/hooks/use_app_toasts'; import type { inputsModel } from '../../../../../common/store'; import { createFilter } from '../../../../../common/containers/helpers'; @@ -77,7 +77,7 @@ export const useNetworkKpiUniquePrivateIps = ({ isInspected: false, refetch: refetch.current, }); - const { addError, addWarning } = useAppToasts(); + const { addError } = useAppToasts(); const networkKpiUniquePrivateIpsSearch = useCallback( (request: NetworkKpiUniquePrivateIpsRequestOptions | null) => { @@ -112,10 +112,6 @@ export const useNetworkKpiUniquePrivateIps = ({ refetch: refetch.current, })); searchSubscription$.current.unsubscribe(); - } else if (isErrorResponse(response)) { - setLoading(false); - addWarning(i18n.ERROR_NETWORK_KPI_UNIQUE_PRIVATE_IPS); - searchSubscription$.current.unsubscribe(); } }, error: (msg) => { @@ -132,7 +128,7 @@ export const useNetworkKpiUniquePrivateIps = ({ asyncSearch(); refetch.current = asyncSearch; }, - [data.search, addError, addWarning, skip] + [data.search, addError, skip] ); useEffect(() => { diff --git a/x-pack/plugins/security_solution/public/explore/users/components/all_users/translations.ts b/x-pack/plugins/security_solution/public/explore/users/components/all_users/translations.ts index 0de194c394a2a..17319a82e74f1 100644 --- a/x-pack/plugins/security_solution/public/explore/users/components/all_users/translations.ts +++ b/x-pack/plugins/security_solution/public/explore/users/components/all_users/translations.ts @@ -43,7 +43,7 @@ export const USER_RISK_TOOLTIP = i18n.translate( 'xpack.securitySolution.usersTable.userRiskToolTip', { defaultMessage: - 'User risk classification is determined by user risk score. Users classified as Critical or High are indicated as risky.', + "The user's risk score determines its risk classification. Risky users are labeled as critical or high.", } ); diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/entities_details.test.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/entities_details.test.tsx index d02f84207ecde..eb56069bb7646 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/entities_details.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/components/entities_details.test.tsx @@ -11,7 +11,12 @@ import '@testing-library/jest-dom'; import { LeftPanelContext } from '../context'; import { TestProviders } from '../../../common/mock'; import { EntitiesDetails } from './entities_details'; -import { ENTITIES_DETAILS_TEST_ID, HOST_DETAILS_TEST_ID, USER_DETAILS_TEST_ID } from './test_ids'; +import { + ENTITIES_DETAILS_NO_DATA_TEST_ID, + ENTITIES_DETAILS_TEST_ID, + HOST_DETAILS_TEST_ID, + USER_DETAILS_TEST_ID, +} from './test_ids'; import { mockContextValue } from '../mocks/mock_context'; import { EXPANDABLE_PANEL_CONTENT_TEST_ID } from '../../shared/components/test_ids'; @@ -49,8 +54,8 @@ describe('', () => { expect(getByTestId(HOST_TEST_ID)).toBeInTheDocument(); }); - it('does not render user and host details if user name and host name are not available', () => { - const { queryByTestId } = render( + it('should render no data message if user name and host name are not available', () => { + const { getByTestId, queryByTestId } = render( ', () => { ); + expect(getByTestId(ENTITIES_DETAILS_NO_DATA_TEST_ID)).toBeInTheDocument(); expect(queryByTestId(USER_TEST_ID)).not.toBeInTheDocument(); expect(queryByTestId(HOST_TEST_ID)).not.toBeInTheDocument(); }); it('does not render user and host details if @timestamp is not available', () => { - const { queryByTestId } = render( + const { getByTestId, queryByTestId } = render( ', () => { ); + expect(getByTestId(ENTITIES_DETAILS_NO_DATA_TEST_ID)).toBeInTheDocument(); expect(queryByTestId(USER_TEST_ID)).not.toBeInTheDocument(); expect(queryByTestId(HOST_TEST_ID)).not.toBeInTheDocument(); }); diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/entities_details.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/entities_details.tsx index 40a3f1823f4d3..ff3678a06e428 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/entities_details.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/components/entities_details.tsx @@ -7,11 +7,12 @@ import React from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { ENTITIES_NO_DATA_MESSAGE } from './translations'; import { useLeftPanelContext } from '../context'; import { getField } from '../../shared/utils'; import { UserDetails } from './user_details'; import { HostDetails } from './host_details'; -import { ENTITIES_DETAILS_TEST_ID } from './test_ids'; +import { ENTITIES_DETAILS_NO_DATA_TEST_ID, ENTITIES_DETAILS_TEST_ID } from './test_ids'; export const ENTITIES_TAB_ID = 'entities-details'; @@ -19,24 +20,34 @@ export const ENTITIES_TAB_ID = 'entities-details'; * Entities displayed in the document details expandable flyout left section under the Insights tab */ export const EntitiesDetails: React.FC = () => { - const { getFieldsData } = useLeftPanelContext(); + const { getFieldsData, scopeId } = useLeftPanelContext(); const hostName = getField(getFieldsData('host.name')); const userName = getField(getFieldsData('user.name')); const timestamp = getField(getFieldsData('@timestamp')); + const showDetails = timestamp && (hostName || userName); + const showUserDetails = userName && timestamp; + const showHostDetails = hostName && timestamp; + return ( - - {userName && timestamp && ( - - - - )} - {hostName && timestamp && ( - - - + <> + {showDetails ? ( + + {showUserDetails && ( + + + + )} + {showHostDetails && ( + + + + )} + + ) : ( +
    {ENTITIES_NO_DATA_MESSAGE}
    )} -
    + ); }; diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/host_details.test.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/host_details.test.tsx index 4ee515e539442..bbec7d1d4d169 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/host_details.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/components/host_details.test.tsx @@ -91,6 +91,7 @@ const timestamp = '2022-07-25T08:20:18.966Z'; const defaultProps = { hostName: 'test host', timestamp, + scopeId: 'scopeId', }; const mockHostDetailsResponse = [ diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/host_details.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/host_details.tsx index 8382e13e6fcc6..c2f9d263ff560 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/host_details.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/components/host_details.tsx @@ -20,6 +20,7 @@ import { EuiPanel, } from '@elastic/eui'; import type { EuiBasicTableColumn } from '@elastic/eui'; +import { getSourcererScopeId } from '../../../helpers'; import { ExpandablePanel } from '../../shared/components/expandable_panel'; import type { RelatedUser } from '../../../../common/search_strategy/security_solution/related_entities/related_users'; import type { RiskSeverity } from '../../../../common/search_strategy'; @@ -67,11 +68,16 @@ export interface HostDetailsProps { * timestamp of alert or event */ timestamp: string; + /** + * Maintain backwards compatibility // TODO remove when possible + */ + scopeId: string; } + /** * Host details and related users, displayed in the document details expandable flyout left section under the Insights tab, Entities tab */ -export const HostDetails: React.FC = ({ hostName, timestamp }) => { +export const HostDetails: React.FC = ({ hostName, timestamp, scopeId }) => { const { to, from, deleteQuery, setQuery, isInitializing } = useGlobalTime(); const { selectedPatterns } = useSourcererDataView(); const dispatch = useDispatch(); @@ -126,14 +132,16 @@ export const HostDetails: React.FC = ({ hostName, timestamp }) render: (user: string) => ( {user} @@ -180,7 +188,7 @@ export const HostDetails: React.FC = ({ hostName, timestamp }) ] : []), ], - [isEntityAnalyticsAuthorized] + [isEntityAnalyticsAuthorized, scopeId] ); const relatedUsersCount = useMemo( diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details.test.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details.test.tsx index a5f2883b676af..e1512b8b7ada1 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details.test.tsx @@ -15,9 +15,19 @@ import { PREVALENCE_DETAILS_TABLE_TEST_ID, } from './test_ids'; import { usePrevalence } from '../../shared/hooks/use_prevalence'; +import { TestProviders } from '../../../common/mock'; jest.mock('../../shared/hooks/use_prevalence'); +const mockDispatch = jest.fn(); +jest.mock('react-redux', () => { + const original = jest.requireActual('react-redux'); + return { + ...original, + useDispatch: () => mockDispatch, + }; +}); + const panelContextValue = { eventId: 'event id', indexName: 'indexName', @@ -53,9 +63,11 @@ describe('PrevalenceDetails', () => { }); const { getByTestId } = render( - - - + + + + + ); expect(getByTestId(PREVALENCE_DETAILS_TABLE_TEST_ID)).toBeInTheDocument(); diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details.tsx index eeadf1362c71e..a5e7907c13c8c 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details.tsx @@ -17,6 +17,7 @@ import { EuiSpacer, EuiSuperDatePicker, } from '@elastic/eui'; +import { InvestigateInTimelineButton } from '../../../common/components/event_details/table/investigate_in_timeline_button'; import type { PrevalenceData } from '../../shared/hooks/use_prevalence'; import { usePrevalence } from '../../shared/hooks/use_prevalence'; import { ERROR_MESSAGE, ERROR_TITLE } from '../../shared/translations'; @@ -46,6 +47,12 @@ import { PREVALENCE_DETAILS_TABLE_TEST_ID, } from './test_ids'; import { useLeftPanelContext } from '../context'; +import { + getDataProvider, + getDataProviderAnd, +} from '../../../common/components/event_details/table/use_action_cell_data_provider'; +import { getEmptyTagValue } from '../../../common/components/empty_value'; +import { IS_OPERATOR } from '../../../../common/types'; export const PREVALENCE_TAB_ID = 'prevalence-details'; const DEFAULT_FROM = 'now-30d'; @@ -63,7 +70,6 @@ const columns: Array> = [ 'data-test-subj': PREVALENCE_DETAILS_TABLE_VALUE_CELL_TEST_ID, }, { - field: 'alertCount', name: ( {PREVALENCE_TABLE_ALERT_COUNT_COLUMN_TITLE} @@ -71,10 +77,25 @@ const columns: Array> = [ ), 'data-test-subj': PREVALENCE_DETAILS_TABLE_ALERT_COUNT_CELL_TEST_ID, + render: (data: PrevalenceData) => { + const dataProviders = [ + getDataProvider(data.field, `timeline-indicator-${data.field}-${data.value}`, data.value), + ]; + return data.alertCount > 0 ? ( + + <>{data.alertCount} + + ) : ( + getEmptyTagValue() + ); + }, width: '10%', }, { - field: 'docCount', name: ( {PREVALENCE_TABLE_DOC_COUNT_COLUMN_TITLE} @@ -82,6 +103,38 @@ const columns: Array> = [ ), 'data-test-subj': PREVALENCE_DETAILS_TABLE_DOC_COUNT_CELL_TEST_ID, + render: (data: PrevalenceData) => { + const dataProviders = [ + { + ...getDataProvider( + data.field, + `timeline-indicator-${data.field}-${data.value}`, + data.value + ), + and: [ + getDataProviderAnd( + 'event.kind', + `timeline-indicator-event.kind-not-signal`, + 'signal', + IS_OPERATOR, + true + ), + ], + }, + ]; + return data.docCount > 0 ? ( + + <>{data.docCount} + + ) : ( + getEmptyTagValue() + ); + }, width: '10%', }, { diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/test_ids.ts b/x-pack/plugins/security_solution/public/flyout/left/components/test_ids.ts index 6c736770544bd..03b768607c20c 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/test_ids.ts +++ b/x-pack/plugins/security_solution/public/flyout/left/components/test_ids.ts @@ -42,6 +42,7 @@ export const PREVALENCE_DETAILS_TABLE_NO_DATA_TEST_ID = /* Entities */ export const ENTITIES_DETAILS_TEST_ID = `${PREFIX}EntitiesDetails` as const; +export const ENTITIES_DETAILS_NO_DATA_TEST_ID = `${ENTITIES_DETAILS_TEST_ID}NoData` as const; export const USER_DETAILS_TEST_ID = `${PREFIX}UsersDetails` as const; export const USER_DETAILS_INFO_TEST_ID = 'user-overview'; export const USER_DETAILS_RELATED_HOSTS_TABLE_TEST_ID = diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/translations.ts b/x-pack/plugins/security_solution/public/flyout/left/components/translations.ts index dbd0cd21e129c..9e7cf56db7c05 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/translations.ts +++ b/x-pack/plugins/security_solution/public/flyout/left/components/translations.ts @@ -7,6 +7,13 @@ import { i18n } from '@kbn/i18n'; +export const ENTITIES_NO_DATA_MESSAGE = i18n.translate( + 'xpack.securitySolution.flyout.entitiesNoDataMessage', + { + defaultMessage: 'No user or host data available', + } +); + export const ANALYZER_ERROR_MESSAGE = i18n.translate( 'xpack.securitySolution.flyout.analyzerErrorMessage', { diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/user_details.test.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/user_details.test.tsx index 51eaf1bb76b74..296ae53fc779a 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/user_details.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/components/user_details.test.tsx @@ -88,6 +88,7 @@ const timestamp = '2022-07-25T08:20:18.966Z'; const defaultProps = { userName: 'test user', timestamp, + scopeId: 'scopeId', }; const mockUserDetailsResponse = [ diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/user_details.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/user_details.tsx index 9e218c7ac94fb..9437858ca211d 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/user_details.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/components/user_details.tsx @@ -20,6 +20,7 @@ import { EuiPanel, } from '@elastic/eui'; import type { EuiBasicTableColumn } from '@elastic/eui'; +import { getSourcererScopeId } from '../../../helpers'; import { ExpandablePanel } from '../../shared/components/expandable_panel'; import type { RelatedHost } from '../../../../common/search_strategy/security_solution/related_entities/related_hosts'; import type { RiskSeverity } from '../../../../common/search_strategy'; @@ -67,11 +68,16 @@ export interface UserDetailsProps { * timestamp of alert or event */ timestamp: string; + /** + * Maintain backwards compatibility // TODO remove when possible + */ + scopeId: string; } + /** * User details and related users, displayed in the document details expandable flyout left section under the Insights tab, Entities tab */ -export const UserDetails: React.FC = ({ userName, timestamp }) => { +export const UserDetails: React.FC = ({ userName, timestamp, scopeId }) => { const { to, from, deleteQuery, setQuery, isInitializing } = useGlobalTime(); const { selectedPatterns } = useSourcererDataView(); const dispatch = useDispatch(); @@ -127,14 +133,16 @@ export const UserDetails: React.FC = ({ userName, timestamp }) render: (host: string) => ( {host} @@ -181,7 +189,7 @@ export const UserDetails: React.FC = ({ userName, timestamp }) ] : []), ], - [isEntityAnalyticsAuthorized] + [isEntityAnalyticsAuthorized, scopeId] ); const relatedHostsCount = useMemo( diff --git a/x-pack/plugins/security_solution/public/flyout/left/context.tsx b/x-pack/plugins/security_solution/public/flyout/left/context.tsx index b5c4f340d5485..15de2dfc9a78d 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/context.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/context.tsx @@ -105,7 +105,7 @@ export const LeftPanelProvider = ({ id, indexName, scopeId, children }: LeftPane dataAsNestedObject, dataFormattedForFieldBrowser, searchHit, - investigationFields: maybeRule?.investigation_fields ?? [], + investigationFields: maybeRule?.investigation_fields?.field_names ?? [], getFieldsData, } : undefined, diff --git a/x-pack/plugins/security_solution/public/flyout/preview/components/alert_reason_preview.tsx b/x-pack/plugins/security_solution/public/flyout/preview/components/alert_reason_preview.tsx index d8791beb7bfd2..985c2bb288fa2 100644 --- a/x-pack/plugins/security_solution/public/flyout/preview/components/alert_reason_preview.tsx +++ b/x-pack/plugins/security_solution/public/flyout/preview/components/alert_reason_preview.tsx @@ -26,7 +26,7 @@ const ReasonPreviewContainer = styled.div``; * Alert reason renderer on a preview panel on top of the right section of expandable flyout */ export const AlertReasonPreview: React.FC = () => { - const { dataAsNestedObject } = usePreviewPanelContext(); + const { dataAsNestedObject, scopeId } = usePreviewPanelContext(); const renderer = useMemo( () => @@ -43,10 +43,10 @@ export const AlertReasonPreview: React.FC = () => { contextId: 'event-details', data: dataAsNestedObject, isDraggable: false, - scopeId: 'global', + scopeId, }) : null, - [renderer, dataAsNestedObject] + [renderer, dataAsNestedObject, scopeId] ); if (!dataAsNestedObject || !renderer) { diff --git a/x-pack/plugins/security_solution/public/flyout/preview/components/rule_preview.tsx b/x-pack/plugins/security_solution/public/flyout/preview/components/rule_preview.tsx index 023c214630dd4..d1595f7419aa0 100644 --- a/x-pack/plugins/security_solution/public/flyout/preview/components/rule_preview.tsx +++ b/x-pack/plugins/security_solution/public/flyout/preview/components/rule_preview.tsx @@ -34,7 +34,11 @@ import * as i18n from './translations'; export const RulePreview: React.FC = memo(() => { const { ruleId, indexPattern } = usePreviewPanelContext(); const [rule, setRule] = useState(null); - const { rule: maybeRule, loading: ruleLoading } = useRuleWithFallback(ruleId ?? ''); + const { + rule: maybeRule, + loading: ruleLoading, + isExistingRule, + } = useRuleWithFallback(ruleId ?? ''); const { data } = useKibana().services; // persist rule until refresh is complete @@ -77,7 +81,7 @@ export const RulePreview: React.FC = memo(() => { return rule ? ( - + ', () => { it('should render title and its components', () => { - const { getByTestId } = render( + const { getByTestId, queryByTestId } = render( @@ -34,5 +36,21 @@ describe('', () => { expect(getByTestId(RULE_PREVIEW_TITLE_TEST_ID)).toBeInTheDocument(); expect(getByTestId(RULE_PREVIEW_RULE_CREATED_BY_TEST_ID)).toBeInTheDocument(); expect(getByTestId(RULE_PREVIEW_RULE_UPDATED_BY_TEST_ID)).toBeInTheDocument(); + expect(queryByTestId(RULE_PREVIEW_RULE_TITLE_SUPPRESSED_TEST_ID)).not.toBeInTheDocument(); + }); + + it('should render deleted rule badge', () => { + const props = { + ...defaultProps, + isSuppressed: true, + }; + const { getByTestId } = render( + + + + + + ); + expect(getByTestId(RULE_PREVIEW_RULE_TITLE_SUPPRESSED_TEST_ID)).toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/preview/components/rule_preview_title.tsx b/x-pack/plugins/security_solution/public/flyout/preview/components/rule_preview_title.tsx index 8a937b5a727af..ab15265d7dc21 100644 --- a/x-pack/plugins/security_solution/public/flyout/preview/components/rule_preview_title.tsx +++ b/x-pack/plugins/security_solution/public/flyout/preview/components/rule_preview_title.tsx @@ -4,14 +4,17 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import React from 'react'; -import { EuiTitle, EuiText, EuiSpacer, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { EuiTitle, EuiText, EuiSpacer, EuiFlexGroup, EuiFlexItem, EuiBadge } from '@elastic/eui'; +import { DELETED_RULE } from '../../../detection_engine/rule_details_ui/pages/rule_details/translations'; import type { Rule } from '../../../detection_engine/rule_management/logic'; import { CreatedBy, UpdatedBy } from '../../../detections/components/rules/rule_info'; import { RULE_PREVIEW_TITLE_TEST_ID, RULE_PREVIEW_RULE_CREATED_BY_TEST_ID, RULE_PREVIEW_RULE_UPDATED_BY_TEST_ID, + RULE_PREVIEW_RULE_TITLE_SUPPRESSED_TEST_ID, } from './test_ids'; interface RulePreviewTitleProps { @@ -19,17 +22,29 @@ interface RulePreviewTitleProps { * Rule object that represents relevant information about a rule */ rule: Rule; + /** + * Flag to indicate if rule is suppressed + */ + isSuppressed: boolean; } /** * Title component that shows basic information of a rule. This is displayed above rule preview body in rule preview panel */ -export const RulePreviewTitle: React.FC = ({ rule }) => { +export const RulePreviewTitle: React.FC = ({ rule, isSuppressed }) => { return (
    {rule.name}
    + {isSuppressed && ( + <> + + + {DELETED_RULE} + + + )} diff --git a/x-pack/plugins/security_solution/public/flyout/preview/components/test_ids.ts b/x-pack/plugins/security_solution/public/flyout/preview/components/test_ids.ts index 764ec90fd9bdf..c9894efe905b5 100644 --- a/x-pack/plugins/security_solution/public/flyout/preview/components/test_ids.ts +++ b/x-pack/plugins/security_solution/public/flyout/preview/components/test_ids.ts @@ -10,6 +10,8 @@ import { CONTENT_TEST_ID, HEADER_TEST_ID } from '../../right/components/expandab /* Rule preview */ export const RULE_PREVIEW_TITLE_TEST_ID = 'securitySolutionDocumentDetailsFlyoutRulePreviewTitle'; +export const RULE_PREVIEW_RULE_TITLE_SUPPRESSED_TEST_ID = + 'securitySolutionDocumentDetailsFlyoutRulePreviewTitleSuppressed'; export const RULE_PREVIEW_RULE_CREATED_BY_TEST_ID = 'securitySolutionDocumentDetailsFlyoutRulePreviewCreatedByText'; export const RULE_PREVIEW_RULE_UPDATED_BY_TEST_ID = diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/entities_overview.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/entities_overview.test.tsx index d26a93262fa31..528b839bb218c 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/entities_overview.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/entities_overview.test.tsx @@ -11,6 +11,7 @@ import { RightPanelContext } from '../context'; import { ENTITIES_HOST_OVERVIEW_TEST_ID, ENTITIES_USER_OVERVIEW_TEST_ID, + INSIGHTS_ENTITIES_NO_DATA_TEST_ID, INSIGHTS_ENTITIES_TEST_ID, } from './test_ids'; import { EntitiesOverview } from './entities_overview'; @@ -28,16 +29,18 @@ const TITLE_LINK_TEST_ID = EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(INSIGHTS_E const TITLE_ICON_TEST_ID = EXPANDABLE_PANEL_HEADER_TITLE_ICON_TEST_ID(INSIGHTS_ENTITIES_TEST_ID); const TITLE_TEXT_TEST_ID = EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(INSIGHTS_ENTITIES_TEST_ID); +const mockContextValue = { + eventId: 'event id', + indexName: 'index', + scopeId: 'scopeId', + getFieldsData: mockGetFieldsData, +} as unknown as RightPanelContext; + describe('', () => { it('should render wrapper component', () => { - const contextValue = { - eventId: 'event id', - getFieldsData: mockGetFieldsData, - } as unknown as RightPanelContext; - const { getByTestId, queryByTestId } = render( - + @@ -51,14 +54,9 @@ describe('', () => { }); it('should render user and host', () => { - const contextValue = { - eventId: 'event id', - getFieldsData: mockGetFieldsData, - } as unknown as RightPanelContext; - const { getByTestId } = render( - + @@ -69,7 +67,7 @@ describe('', () => { it('should only render user when host name is null', () => { const contextValue = { - eventId: 'event id', + ...mockContextValue, getFieldsData: (field: string) => (field === 'user.name' ? 'user1' : null), } as unknown as RightPanelContext; @@ -87,7 +85,7 @@ describe('', () => { it('should only render host when user name is null', () => { const contextValue = { - eventId: 'event id', + ...mockContextValue, getFieldsData: (field: string) => (field === 'host.name' ? 'host1' : null), } as unknown as RightPanelContext; @@ -103,13 +101,13 @@ describe('', () => { expect(queryByTestId(ENTITIES_USER_OVERVIEW_TEST_ID)).not.toBeInTheDocument(); }); - it('should not render if both host name and user name are null/blank', () => { + it('should render no data message if both host name and user name are null/blank', () => { const contextValue = { - eventId: 'event id', + ...mockContextValue, getFieldsData: (field: string) => {}, } as unknown as RightPanelContext; - const { container } = render( + const { queryByTestId } = render( @@ -117,13 +115,47 @@ describe('', () => { ); - expect(container).toBeEmptyDOMElement(); + expect(queryByTestId(INSIGHTS_ENTITIES_NO_DATA_TEST_ID)).toBeInTheDocument(); }); it('should not render if eventId is null', () => { const contextValue = { + ...mockContextValue, eventId: null, - getFieldsData: (field: string) => {}, + } as unknown as RightPanelContext; + + const { container } = render( + + + + + + ); + + expect(container).toBeEmptyDOMElement(); + }); + + it('should not render if indexName is null', () => { + const contextValue = { + ...mockContextValue, + indexName: null, + } as unknown as RightPanelContext; + + const { container } = render( + + + + + + ); + + expect(container).toBeEmptyDOMElement(); + }); + + it('should not render if scopeId is null', () => { + const contextValue = { + ...mockContextValue, + scopeId: null, } as unknown as RightPanelContext; const { container } = render( diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/entities_overview.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/entities_overview.tsx index d74fd844ea4a9..efab7fa9f6d03 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/entities_overview.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/entities_overview.tsx @@ -8,10 +8,10 @@ import React, { useCallback } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { useExpandableFlyoutContext } from '@kbn/expandable-flyout'; +import { INSIGHTS_ENTITIES_NO_DATA_TEST_ID, INSIGHTS_ENTITIES_TEST_ID } from './test_ids'; import { ExpandablePanel } from '../../shared/components/expandable_panel'; import { useRightPanelContext } from '../context'; -import { INSIGHTS_ENTITIES_TEST_ID } from './test_ids'; -import { ENTITIES_TITLE } from './translations'; +import { ENTITIES_NO_DATA_MESSAGE, ENTITIES_TITLE } from './translations'; import { getField } from '../../shared/utils'; import { HostEntityOverview } from './host_entity_overview'; import { UserEntityOverview } from './user_entity_overview'; @@ -42,7 +42,7 @@ export const EntitiesOverview: React.FC = () => { }); }, [eventId, openLeftPanel, indexName, scopeId]); - if (!eventId || (!userName && !hostName)) { + if (!eventId || !indexName || !scopeId) { return null; } @@ -56,19 +56,23 @@ export const EntitiesOverview: React.FC = () => { }} data-test-subj={INSIGHTS_ENTITIES_TEST_ID} > - - {userName && ( - - - - )} - - {hostName && ( - - - - )} - + {userName || hostName ? ( + + {userName && ( + + + + )} + + {hostName && ( + + + + )} + + ) : ( +
    {ENTITIES_NO_DATA_MESSAGE}
    + )} ); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/header_title.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/header_title.test.tsx index 6d132ce54501e..3256d3c3895cd 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/header_title.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/header_title.test.tsx @@ -23,16 +23,23 @@ import { useDateFormat, useTimeZone } from '../../../common/lib/kibana'; import { mockDataFormattedForFieldBrowser, mockGetFieldsData } from '../mocks/mock_context'; import { useAssistant } from '../hooks/use_assistant'; import { TestProvidersComponent } from '../../../common/mock'; +import { useGetAlertDetailsFlyoutLink } from '../../../timelines/components/side_panel/event_details/use_get_alert_details_flyout_link'; jest.mock('../../../common/lib/kibana'); jest.mock('../hooks/use_assistant'); +jest.mock( + '../../../timelines/components/side_panel/event_details/use_get_alert_details_flyout_link' +); moment.suppressDeprecationWarnings = true; moment.tz.setDefault('UTC'); const dateFormat = 'MMM D, YYYY @ HH:mm:ss.SSS'; - const flyoutContextValue = {} as unknown as ExpandableFlyoutContext; +const mockContextValue = { + dataFormattedForFieldBrowser: mockDataFormattedForFieldBrowser, + getFieldsData: jest.fn().mockImplementation(mockGetFieldsData), +} as unknown as RightPanelContext; const renderHeader = (contextValue: RightPanelContext) => render( @@ -50,15 +57,11 @@ describe('', () => { jest.mocked(useDateFormat).mockImplementation(() => dateFormat); jest.mocked(useTimeZone).mockImplementation(() => 'UTC'); jest.mocked(useAssistant).mockReturnValue({ showAssistant: true, promptContextId: '' }); + jest.mocked(useGetAlertDetailsFlyoutLink).mockReturnValue('url'); }); it('should render component', () => { - const contextValue = { - dataFormattedForFieldBrowser: mockDataFormattedForFieldBrowser, - getFieldsData: jest.fn().mockImplementation(mockGetFieldsData), - } as unknown as RightPanelContext; - - const { getByTestId } = renderHeader(contextValue); + const { getByTestId } = renderHeader(mockContextValue); expect(getByTestId(FLYOUT_HEADER_TITLE_TEST_ID)).toBeInTheDocument(); expect(getByTestId(FLYOUT_HEADER_RISK_SCORE_VALUE_TEST_ID)).toBeInTheDocument(); @@ -66,101 +69,42 @@ describe('', () => { }); it('should render rule name in the title if document is an alert', () => { - const contextValue = { - dataFormattedForFieldBrowser: [ - { - category: 'kibana', - field: 'kibana.alert.rule.uuid', - values: ['123'], - originalValue: ['123'], - isObjectArray: false, - }, - { - category: 'kibana', - field: 'kibana.alert.rule.name', - values: ['test'], - originalValue: ['test'], - isObjectArray: false, - }, - ], - getFieldsData: () => [], - } as unknown as RightPanelContext; + const { getByTestId } = renderHeader(mockContextValue); - const { getByTestId } = renderHeader(contextValue); - - expect(getByTestId(FLYOUT_HEADER_TITLE_TEST_ID)).toHaveTextContent('test'); + expect(getByTestId(FLYOUT_HEADER_TITLE_TEST_ID)).toHaveTextContent('rule-name'); }); - it('should render share button in the title if document is an alert with url info', () => { - const contextValue = { - dataFormattedForFieldBrowser: [ - { - category: 'kibana', - field: 'kibana.alert.rule.uuid', - values: ['123'], - originalValue: ['123'], - isObjectArray: false, - }, - { - category: 'kibana', - field: 'kibana.alert.url', - values: ['http://kibana.url/alert/id'], - originalValue: ['http://kibana.url/alert/id'], - isObjectArray: false, - }, - ], - getFieldsData: () => [], - } as unknown as RightPanelContext; - - const { getByTestId } = renderHeader(contextValue); + it('should render share button in the title', () => { + const { getByTestId } = renderHeader(mockContextValue); expect(getByTestId(FLYOUT_HEADER_SHARE_BUTTON_TEST_ID)).toBeInTheDocument(); }); it('should not render share button in the title if alert is missing url info', () => { - const contextValue = { - dataFormattedForFieldBrowser: [ - { - category: 'kibana', - field: 'kibana.alert.rule.uuid', - values: ['123'], - originalValue: ['123'], - isObjectArray: false, - }, - ], - getFieldsData: () => [], - } as unknown as RightPanelContext; + jest.mocked(useGetAlertDetailsFlyoutLink).mockReturnValue(null); - const { queryByTestId } = renderHeader(contextValue); + const { queryByTestId } = renderHeader(mockContextValue); expect(queryByTestId(FLYOUT_HEADER_SHARE_BUTTON_TEST_ID)).not.toBeInTheDocument(); }); it('should render chat button in the title', () => { - const contextValue = { - dataFormattedForFieldBrowser: [], - getFieldsData: () => [], - } as unknown as RightPanelContext; - - const { getByTestId } = renderHeader(contextValue); + const { getByTestId } = renderHeader(mockContextValue); expect(getByTestId(FLYOUT_HEADER_CHAT_BUTTON_TEST_ID)).toBeInTheDocument(); }); it('should not render chat button in the title if should not be shown', () => { jest.mocked(useAssistant).mockReturnValue({ showAssistant: false, promptContextId: '' }); - const contextValue = { - dataFormattedForFieldBrowser: [], - getFieldsData: () => [], - } as unknown as RightPanelContext; - const { queryByTestId } = renderHeader(contextValue); + const { queryByTestId } = renderHeader(mockContextValue); expect(queryByTestId(FLYOUT_HEADER_CHAT_BUTTON_TEST_ID)).not.toBeInTheDocument(); }); it('should render default document detail title if document is not an alert', () => { const contextValue = { + ...mockContextValue, dataFormattedForFieldBrowser: [ { category: 'kibana', @@ -170,7 +114,6 @@ describe('', () => { isObjectArray: false, }, ], - getFieldsData: () => [], } as unknown as RightPanelContext; const { getByTestId } = renderHeader(contextValue); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/header_title.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/header_title.tsx index bac1b3d9f1bd1..716db70b60947 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/header_title.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/header_title.tsx @@ -11,6 +11,7 @@ import { NewChatById } from '@kbn/elastic-assistant'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui'; import { isEmpty } from 'lodash'; import { css } from '@emotion/react'; +import { useGetAlertDetailsFlyoutLink } from '../../../timelines/components/side_panel/event_details/use_get_alert_details_flyout_link'; import { DocumentStatus } from './status'; import { useAssistant } from '../hooks/use_assistant'; import { @@ -37,15 +38,22 @@ export interface HeaderTitleProps { * Document details flyout right section header */ export const HeaderTitle: VFC = memo(({ flyoutIsExpandable }) => { - const { dataFormattedForFieldBrowser } = useRightPanelContext(); - const { isAlert, ruleName, timestamp, alertUrl } = useBasicDataFromDetailsData( + const { dataFormattedForFieldBrowser, eventId, indexName } = useRightPanelContext(); + const { isAlert, ruleName, timestamp } = useBasicDataFromDetailsData( dataFormattedForFieldBrowser ); + const alertDetailsLink = useGetAlertDetailsFlyoutLink({ + _id: eventId, + _index: indexName, + timestamp, + }); + + const showShareAlertButton = isAlert && alertDetailsLink; + const { showAssistant, promptContextId } = useAssistant({ dataFormattedForFieldBrowser, isAlert, }); - const showShareAlertButton = isAlert && alertUrl; return ( <> @@ -71,7 +79,7 @@ export const HeaderTitle: VFC = memo(({ flyoutIsExpandable }) )} {showShareAlertButton && ( - + )}
    diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.test.tsx index 0d8ed18d83a43..2f21aab420af9 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.test.tsx @@ -20,12 +20,13 @@ jest.mock('../../../detection_engine/rule_management/logic/use_rule_with_fallbac describe('', () => { beforeEach(() => { - (useRuleWithFallback as jest.Mock).mockReturnValue({ investigation_fields: [] }); + (useRuleWithFallback as jest.Mock).mockReturnValue({ investigation_fields: undefined }); }); it('should render the component', () => { const panelContextValue = { dataFormattedForFieldBrowser: mockDataFormattedForFieldBrowser, + scopeId: 'scopeId', } as unknown as RightPanelContext; (useHighlightedFields as jest.Mock).mockReturnValue({ field: { @@ -48,6 +49,7 @@ describe('', () => { it(`should render empty component if there aren't any highlighted fields`, () => { const panelContextValue = { dataFormattedForFieldBrowser: mockDataFormattedForFieldBrowser, + scopeId: 'scopeId', } as unknown as RightPanelContext; (useHighlightedFields as jest.Mock).mockReturnValue({}); @@ -63,6 +65,7 @@ describe('', () => { it('should render empty component if dataFormattedForFieldBrowser is null', () => { const panelContextValue = { dataFormattedForFieldBrowser: null, + scopeId: 'scopeId', } as unknown as RightPanelContext; (useHighlightedFields as jest.Mock).mockReturnValue({ field: { diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.tsx index 79f75ba7ff861..4a0405c06b7e1 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.tsx @@ -9,6 +9,7 @@ import type { FC } from 'react'; import React, { useMemo } from 'react'; import type { EuiBasicTableColumn } from '@elastic/eui'; import { EuiFlexGroup, EuiFlexItem, EuiInMemoryTable, EuiPanel, EuiTitle } from '@elastic/eui'; +import { getSourcererScopeId } from '../../../helpers'; import { convertHighlightedFieldsToTableRow } from '../../shared/utils/highlighted_fields_helpers'; import { useRuleWithFallback } from '../../../detection_engine/rule_management/logic/use_rule_with_fallback'; import { useBasicDataFromDetailsData } from '../../../timelines/components/side_panel/event_details/helpers'; @@ -41,6 +42,10 @@ export interface HighlightedFieldsTableRow { * Highlighted field value */ values: string[] | null | undefined; + /** + * Maintain backwards compatibility // TODO remove when possible + */ + scopeId: string; }; } @@ -54,15 +59,21 @@ const columns: Array> = [ field: 'description', name: HIGHLIGHTED_FIELDS_VALUE_COLUMN, 'data-test-subj': 'valueCell', - render: (description: { field: string; values: string[] | null | undefined }) => ( + render: (description: { + field: string; + values: string[] | null | undefined; + scopeId: string; + }) => ( @@ -74,17 +85,17 @@ const columns: Array> = [ * Component that displays the highlighted fields in the right panel under the Investigation section. */ export const HighlightedFields: FC = () => { - const { dataFormattedForFieldBrowser } = useRightPanelContext(); + const { dataFormattedForFieldBrowser, scopeId } = useRightPanelContext(); const { ruleId } = useBasicDataFromDetailsData(dataFormattedForFieldBrowser); const { rule: maybeRule } = useRuleWithFallback(ruleId); const highlightedFields = useHighlightedFields({ dataFormattedForFieldBrowser, - investigationFields: maybeRule?.investigation_fields ?? [], + investigationFields: maybeRule?.investigation_fields?.field_names ?? [], }); const items = useMemo( - () => convertHighlightedFieldsToTableRow(highlightedFields), - [highlightedFields] + () => convertHighlightedFieldsToTableRow(highlightedFields, scopeId), + [highlightedFields, scopeId] ); if (!dataFormattedForFieldBrowser || items.length === 0) { diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/host_entity_overview.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/host_entity_overview.test.tsx index 8b61c823fb299..f737cfc006419 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/host_entity_overview.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/host_entity_overview.test.tsx @@ -96,7 +96,7 @@ describe('', () => { ); expect(getByTestId(ENTITIES_HOST_OVERVIEW_OS_FAMILY_TEST_ID)).toHaveTextContent('—'); - expect(getByTestId(ENTITIES_HOST_OVERVIEW_RISK_LEVEL_TEST_ID)).toHaveTextContent('Unknown'); + expect(getByTestId(ENTITIES_HOST_OVERVIEW_RISK_LEVEL_TEST_ID)).toHaveTextContent('—'); }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/host_entity_overview.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/host_entity_overview.tsx index 18cac803894a9..88804ffb97d12 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/host_entity_overview.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/host_entity_overview.tsx @@ -13,6 +13,7 @@ import { EuiIcon, useEuiTheme, useEuiFontSize, + EuiIconTip, } from '@elastic/eui'; import { css } from '@emotion/css'; import { getOr } from 'lodash/fp'; @@ -23,11 +24,8 @@ import { FirstLastSeen, FirstLastSeenType, } from '../../../common/components/first_last_seen/first_last_seen'; -import { - buildHostNamesFilter, - RiskScoreEntity, - RiskSeverity, -} from '../../../../common/search_strategy'; +import { buildHostNamesFilter, RiskScoreEntity } from '../../../../common/search_strategy'; +import { getEmptyTagValue } from '../../../common/components/empty_value'; import { DefaultFieldRenderer } from '../../../timelines/components/field_renderers/field_renderers'; import { DescriptionListStyled } from '../../../common/components/page'; import { OverviewDescriptionList } from '../../../common/components/overview_description_list'; @@ -44,7 +42,9 @@ import { ENTITIES_HOST_OVERVIEW_LAST_SEEN_TEST_ID, ENTITIES_HOST_OVERVIEW_RISK_LEVEL_TEST_ID, ENTITIES_HOST_OVERVIEW_LINK_TEST_ID, + TECHNICAL_PREVIEW_ICON_TEST_ID, } from './test_ids'; +import { TECHNICAL_PREVIEW_TITLE, TECHNICAL_PREVIEW_MESSAGE } from './translations'; import { LeftPanelInsightsTab, LeftPanelKey } from '../../left'; const HOST_ICON = 'storage'; @@ -146,13 +146,28 @@ export const HostEntityOverview: React.FC = ({ hostName const hostRiskData = hostRisk && hostRisk.length > 0 ? hostRisk[0] : undefined; return [ { - title: i18n.HOST_RISK_CLASSIFICATION, + title: ( + <> + {i18n.HOST_RISK_CLASSIFICATION} + + + ), description: ( <> {hostRiskData ? ( ) : ( - + getEmptyTagValue() )} ), diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/severity.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/severity.test.tsx index ed56178531de6..d599236b4531d 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/severity.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/severity.test.tsx @@ -20,6 +20,7 @@ describe('', () => { it('should render severity information', () => { const contextValue = { getFieldsData: jest.fn().mockImplementation(mockGetFieldsData), + scopeId: 'scopeId', } as unknown as RightPanelContext; const { getByTestId } = render( @@ -39,6 +40,7 @@ describe('', () => { it('should render empty component if missing getFieldsData value', () => { const contextValue = { getFieldsData: jest.fn(), + scopeId: 'scopeId', } as unknown as RightPanelContext; const { container } = render( @@ -53,6 +55,7 @@ describe('', () => { it('should render empty component if getFieldsData is invalid array', () => { const contextValue = { getFieldsData: jest.fn().mockImplementation(() => ['abc']), + scopeId: 'scopeId', } as unknown as RightPanelContext; const { container } = render( @@ -67,6 +70,7 @@ describe('', () => { it('should render empty component if getFieldsData is invalid string', () => { const contextValue = { getFieldsData: jest.fn().mockImplementation(() => 'abc'), + scopeId: 'scopeId', } as unknown as RightPanelContext; const { container } = render( diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/severity.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/severity.tsx index 9059b24a6fd2b..c5f49400f088c 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/severity.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/severity.tsx @@ -11,6 +11,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui'; import { ALERT_SEVERITY } from '@kbn/rule-data-utils'; import type { Severity } from '@kbn/securitysolution-io-ts-alerting-types'; import { CellActionsMode } from '@kbn/cell-actions'; +import { getSourcererScopeId } from '../../../helpers'; import { SecurityCellActions } from '../../../common/components/cell_actions'; import { SecurityCellActionsTrigger } from '../../../actions/constants'; import { SEVERITY_TITLE } from './translations'; @@ -25,7 +26,7 @@ const isSeverity = (x: unknown): x is Severity => * Document details severity displayed in flyout right section header */ export const DocumentSeverity: FC = memo(() => { - const { getFieldsData } = useRightPanelContext(); + const { getFieldsData, scopeId } = useRightPanelContext(); const fieldsData = getFieldsData(ALERT_SEVERITY); if (!fieldsData) { @@ -55,8 +56,10 @@ export const DocumentSeverity: FC = memo(() => { value: alertSeverity, }} mode={CellActionsMode.HOVER_RIGHT} - triggerId={SecurityCellActionsTrigger.DEFAULT} - visibleCellActions={6} + triggerId={SecurityCellActionsTrigger.DEFAULT} // TODO use SecurityCellActionsTrigger.DETAILS_FLYOUT when https://github.com/elastic/kibana/issues/155243 is fixed + visibleCellActions={5} // TODO use 6 when https://github.com/elastic/kibana/issues/155243 is fixed + sourcererScopeId={getSourcererScopeId(scopeId)} + metadata={{ scopeId }} > diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/status.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/status.tsx index dacb68435fcb1..fa3a0c6f2ab14 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/status.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/status.tsx @@ -10,6 +10,7 @@ import React, { useMemo } from 'react'; import { find } from 'lodash/fp'; import { useExpandableFlyoutContext } from '@kbn/expandable-flyout'; import { CellActionsMode } from '@kbn/cell-actions'; +import { getSourcererScopeId } from '../../../helpers'; import { SecurityCellActions } from '../../../common/components/cell_actions'; import type { EnrichedFieldInfo, @@ -61,8 +62,10 @@ export const DocumentStatus: FC = () => { value: statusData.values[0], }} mode={CellActionsMode.HOVER_RIGHT} - triggerId={SecurityCellActionsTrigger.DEFAULT} - visibleCellActions={6} + triggerId={SecurityCellActionsTrigger.DEFAULT} // TODO use SecurityCellActionsTrigger.DETAILS_FLYOUT when https://github.com/elastic/kibana/issues/155243 is fixed + visibleCellActions={5} // TODO use 6 when https://github.com/elastic/kibana/issues/155243 is fixed + sourcererScopeId={getSourcererScopeId(scopeId)} + metadata={{ scopeId }} > `${dataTestSu /* Insights Entities */ export const INSIGHTS_ENTITIES_TEST_ID = 'securitySolutionDocumentDetailsFlyoutInsightsEntities'; +export const INSIGHTS_ENTITIES_NO_DATA_TEST_ID = `${INSIGHTS_ENTITIES_TEST_ID}NoData` as const; export const ENTITIES_USER_OVERVIEW_TEST_ID = 'securitySolutionDocumentDetailsFlyoutEntitiesUserOverview'; export const ENTITIES_USER_OVERVIEW_LINK_TEST_ID = `${ENTITIES_USER_OVERVIEW_TEST_ID}Link`; @@ -95,6 +96,8 @@ export const ENTITIES_HOST_OVERVIEW_LINK_TEST_ID = `${ENTITIES_HOST_OVERVIEW_TES export const ENTITIES_HOST_OVERVIEW_OS_FAMILY_TEST_ID = `${ENTITIES_HOST_OVERVIEW_TEST_ID}OsFamily`; export const ENTITIES_HOST_OVERVIEW_LAST_SEEN_TEST_ID = `${ENTITIES_HOST_OVERVIEW_TEST_ID}LastSeen`; export const ENTITIES_HOST_OVERVIEW_RISK_LEVEL_TEST_ID = `${ENTITIES_HOST_OVERVIEW_TEST_ID}RiskLevel`; +export const TECHNICAL_PREVIEW_ICON_TEST_ID = + 'securitySolutionDocumentDetailsFlyoutTechnicalPreviewIcon'; /* Insights Threat Intelligence */ diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts b/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts index 24df07b006401..4d1390701ad10 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts +++ b/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts @@ -130,6 +130,13 @@ export const ENTITIES_TITLE = i18n.translate( { defaultMessage: 'Entities' } ); +export const ENTITIES_NO_DATA_MESSAGE = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.entitiesNoDataMessage', + { + defaultMessage: 'No user or host data available', + } +); + export const THREAT_INTELLIGENCE_TITLE = i18n.translate( 'xpack.securitySolution.flyout.documentDetails.threatIntelligenceTitle', { defaultMessage: 'Threat Intelligence' } @@ -300,6 +307,11 @@ export const RESPONSE_EMPTY = i18n.translate('xpack.securitySolution.flyout.resp defaultMessage: 'There are no response actions defined for this event.', }); +export const TECHNICAL_PREVIEW_TITLE = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.technicalPreviewTitle', + { defaultMessage: 'Technical preview' } +); + export const TECHNICAL_PREVIEW_MESSAGE = i18n.translate( 'xpack.securitySolution.flyout.documentDetails.technicalPreviewMessage', { diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/user_entity_overview.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/user_entity_overview.test.tsx index 4739130949e96..821b0ee5d19f6 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/user_entity_overview.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/user_entity_overview.test.tsx @@ -96,7 +96,7 @@ describe('', () => { ); expect(getByTestId(ENTITIES_USER_OVERVIEW_DOMAIN_TEST_ID)).toHaveTextContent('—'); - expect(getByTestId(ENTITIES_USER_OVERVIEW_RISK_LEVEL_TEST_ID)).toHaveTextContent('Unknown'); + expect(getByTestId(ENTITIES_USER_OVERVIEW_RISK_LEVEL_TEST_ID)).toHaveTextContent('—'); }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/user_entity_overview.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/user_entity_overview.tsx index ed3da595b7a21..998f6f71b02a7 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/user_entity_overview.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/user_entity_overview.tsx @@ -13,6 +13,7 @@ import { EuiLink, useEuiTheme, useEuiFontSize, + EuiIconTip, } from '@elastic/eui'; import { css } from '@emotion/css'; import { getOr } from 'lodash/fp'; @@ -25,11 +26,8 @@ import { FirstLastSeen, FirstLastSeenType, } from '../../../common/components/first_last_seen/first_last_seen'; -import { - buildUserNamesFilter, - RiskScoreEntity, - RiskSeverity, -} from '../../../../common/search_strategy'; +import { buildUserNamesFilter, RiskScoreEntity } from '../../../../common/search_strategy'; +import { getEmptyTagValue } from '../../../common/components/empty_value'; import { DefaultFieldRenderer } from '../../../timelines/components/field_renderers/field_renderers'; import { DescriptionListStyled } from '../../../common/components/page'; import { OverviewDescriptionList } from '../../../common/components/overview_description_list'; @@ -44,7 +42,9 @@ import { ENTITIES_USER_OVERVIEW_LAST_SEEN_TEST_ID, ENTITIES_USER_OVERVIEW_RISK_LEVEL_TEST_ID, ENTITIES_USER_OVERVIEW_LINK_TEST_ID, + TECHNICAL_PREVIEW_ICON_TEST_ID, } from './test_ids'; +import { TECHNICAL_PREVIEW_TITLE, TECHNICAL_PREVIEW_MESSAGE } from './translations'; import { useObservedUserDetails } from '../../../explore/users/containers/users/observed_details'; const USER_ICON = 'user'; @@ -145,13 +145,28 @@ export const UserEntityOverview: React.FC = ({ userName return [ { - title: i18n.USER_RISK_CLASSIFICATION, + title: ( + <> + {i18n.USER_RISK_CLASSIFICATION} + + + ), description: ( <> {userRiskData ? ( ) : ( - + getEmptyTagValue() )} ), diff --git a/x-pack/plugins/security_solution/public/flyout/right/context.tsx b/x-pack/plugins/security_solution/public/flyout/right/context.tsx index 31eec77707d2f..fb9424314deb2 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/context.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/context.tsx @@ -117,7 +117,7 @@ export const RightPanelProvider = ({ dataAsNestedObject, dataFormattedForFieldBrowser, searchHit, - investigationFields: maybeRule?.investigation_fields ?? [], + investigationFields: maybeRule?.investigation_fields?.field_names ?? [], refetchFlyoutData, getFieldsData, } diff --git a/x-pack/plugins/security_solution/public/flyout/shared/utils/highlighted_fields_helpers.test.ts b/x-pack/plugins/security_solution/public/flyout/shared/utils/highlighted_fields_helpers.test.ts index f79a7a300eb3e..ec92745455e21 100644 --- a/x-pack/plugins/security_solution/public/flyout/shared/utils/highlighted_fields_helpers.test.ts +++ b/x-pack/plugins/security_solution/public/flyout/shared/utils/highlighted_fields_helpers.test.ts @@ -10,6 +10,8 @@ import { convertHighlightedFieldsToTableRow, } from './highlighted_fields_helpers'; +const scopeId = 'scopeId'; + describe('convertHighlightedFieldsToTableRow', () => { it('should convert highlighted fields to a table row', () => { const highlightedFields = { @@ -17,12 +19,13 @@ describe('convertHighlightedFieldsToTableRow', () => { values: ['host-1'], }, }; - expect(convertHighlightedFieldsToTableRow(highlightedFields)).toEqual([ + expect(convertHighlightedFieldsToTableRow(highlightedFields, scopeId)).toEqual([ { field: 'host.name', description: { field: 'host.name', values: ['host-1'], + scopeId: 'scopeId', }, }, ]); @@ -35,12 +38,13 @@ describe('convertHighlightedFieldsToTableRow', () => { values: ['host-1'], }, }; - expect(convertHighlightedFieldsToTableRow(highlightedFields)).toEqual([ + expect(convertHighlightedFieldsToTableRow(highlightedFields, scopeId)).toEqual([ { field: 'host.name-override', description: { field: 'host.name-override', values: ['host-1'], + scopeId: 'scopeId', }, }, ]); diff --git a/x-pack/plugins/security_solution/public/flyout/shared/utils/highlighted_fields_helpers.ts b/x-pack/plugins/security_solution/public/flyout/shared/utils/highlighted_fields_helpers.ts index d44ec666a27bc..094187b570251 100644 --- a/x-pack/plugins/security_solution/public/flyout/shared/utils/highlighted_fields_helpers.ts +++ b/x-pack/plugins/security_solution/public/flyout/shared/utils/highlighted_fields_helpers.ts @@ -12,9 +12,11 @@ import type { HighlightedFieldsTableRow } from '../../right/components/highlight /** * Converts the highlighted fields to a format that can be consumed by the HighlightedFields component * @param highlightedFields + * @param scopeId */ export const convertHighlightedFieldsToTableRow = ( - highlightedFields: UseHighlightedFieldsResult + highlightedFields: UseHighlightedFieldsResult, + scopeId: string ): HighlightedFieldsTableRow[] => { const fieldNames = Object.keys(highlightedFields); return fieldNames.map((fieldName) => { @@ -27,6 +29,7 @@ export const convertHighlightedFieldsToTableRow = ( description: { field, values, + scopeId, }, }; }); diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_ti_data_sources.ts b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_ti_data_sources.ts index 43aff52970348..96a0c44327909 100644 --- a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_ti_data_sources.ts +++ b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_ti_data_sources.ts @@ -9,7 +9,7 @@ import { filter } from 'rxjs/operators'; import { useEffect, useState } from 'react'; import { useObservable, withOptionalSignal } from '@kbn/securitysolution-hook-utils'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import { isCompleteResponse, isErrorResponse } from '@kbn/data-plugin/public'; +import { isCompleteResponse } from '@kbn/data-plugin/public'; import { useKibana } from '../../../common/lib/kibana'; import type { Bucket, @@ -51,7 +51,7 @@ export const getTiDataSourcesComplete = ( ): Observable => { return getTiDataSources(props).pipe( filter((response) => { - return isErrorResponse(response) || isCompleteResponse(response); + return isCompleteResponse(response); }) ); }; diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index cd647ef499260..c482c7cfa1616 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -16,8 +16,9 @@ import type { PluginInitializerContext, Plugin as IPlugin, } from '@kbn/core/public'; + import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import { FilterManager, NowProvider, QueryService } from '@kbn/data-plugin/public'; +import { NowProvider, QueryService } from '@kbn/data-plugin/public'; import { DEFAULT_APP_CATEGORIES, AppNavLinkStatus } from '@kbn/core/public'; import { Storage } from '@kbn/kibana-utils-plugin/public'; import type { FleetUiExtensionGetterOptions } from './management/pages/policy/view/ingest_manager_integration/types'; @@ -169,17 +170,17 @@ export class Plugin implements IPlugin { const Rules = React.memo(RulesContainerComponent); -const CoverageOverviewRoutes = () => { - const isDetectionsCoverageOverviewEnabled = useIsExperimentalFeatureEnabled( - 'detectionsCoverageOverview' - ); - - return isDetectionsCoverageOverviewEnabled ? ( - - - - - - ) : ( - - ); -}; +const CoverageOverviewRoutes = () => ( + + + + + +); export const routes: SecuritySubPluginRoutes = [ { diff --git a/x-pack/plugins/security_solution/public/threat_intelligence/links.ts b/x-pack/plugins/security_solution/public/threat_intelligence/links.ts index 87d49e5f459a3..8b2b8554e36ce 100644 --- a/x-pack/plugins/security_solution/public/threat_intelligence/links.ts +++ b/x-pack/plugins/security_solution/public/threat_intelligence/links.ts @@ -17,6 +17,6 @@ import type { LinkItem } from '../common/links'; */ export const indicatorsLinks: LinkItem = { ...getSecuritySolutionLink('indicators'), - globalNavPosition: 6, + globalNavPosition: 7, capabilities: [`${SERVER_APP_ID}.threat-intelligence`], }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/customizations/mock.data.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/customizations/mock.data.ts new file mode 100644 index 0000000000000..d43f1145c6e65 --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/customizations/mock.data.ts @@ -0,0 +1,547 @@ +/* + * 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 type { BrushTriggerEvent } from '@kbn/charts-plugin/public'; +import type { ClickTriggerEventData } from './use_histogram_customizations'; + +export const mockOnMultiValueFilterCallbackEventData = { + data: [ + { + cells: [ + { + row: 0, + column: 0, + }, + ], + relation: 'OR', + table: { + type: 'datatable', + columns: [ + { + id: 'breakdown_column', + name: 'Top 3 values of event.module', + meta: { + type: 'string', + field: 'event.module', + index: + '.alerts-security.alerts-default,apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,traces-apm*,winlogbeat-*,-*elastic-cloud-logs-*', + params: { + id: 'terms', + params: { + id: 'string', + otherBucketLabel: 'Other', + missingBucketLabel: '(missing value)', + }, + }, + source: 'esaggs', + sourceParams: { + hasPrecisionError: false, + indexPatternId: 'security-solution-default', + id: '0', + enabled: true, + type: 'terms', + params: { + field: 'event.module', + orderBy: '2', + order: 'desc', + size: 3, + otherBucket: true, + otherBucketLabel: 'Other', + missingBucket: false, + missingBucketLabel: '(missing value)', + includeIsRegex: false, + excludeIsRegex: false, + }, + schema: 'segment', + }, + }, + }, + { + id: 'date_column', + name: '@timestamp per 30 seconds', + meta: { + type: 'date', + field: '@timestamp', + index: + '.alerts-security.alerts-default,apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,traces-apm*,winlogbeat-*,-*elastic-cloud-logs-*', + params: { + id: 'date', + params: { + pattern: 'HH:mm:ss', + }, + }, + source: 'esaggs', + sourceParams: { + hasPrecisionError: false, + indexPatternId: 'security-solution-default', + appliedTimeRange: { + from: '2023-08-17T09:25:24.869Z', + to: '2023-08-17T09:40:24.869Z', + }, + id: '1', + enabled: true, + type: 'date_histogram', + params: { + field: '@timestamp', + timeRange: { + from: '2023-08-17T09:25:24.869Z', + to: '2023-08-17T09:40:24.869Z', + }, + useNormalizedEsInterval: true, + extendToTimeRange: false, + scaleMetricValues: false, + interval: 'auto', + used_interval: '30s', + drop_partials: false, + min_doc_count: 1, + extended_bounds: {}, + }, + schema: 'segment', + }, + }, + }, + { + id: 'count_column', + name: 'Count of records', + meta: { + type: 'number', + index: + '.alerts-security.alerts-default,apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,traces-apm*,winlogbeat-*,-*elastic-cloud-logs-*', + params: { + id: 'number', + params: { + pattern: '0,0', + formatOverride: true, + }, + }, + source: 'esaggs', + sourceParams: { + hasPrecisionError: false, + indexPatternId: 'security-solution-default', + id: '2', + enabled: true, + type: 'count', + params: { + emptyAsNull: false, + }, + schema: 'metric', + }, + }, + }, + ], + rows: [ + { + breakdown_column: 'endpoint', + date_column: 1692264810000, + count_column: 2, + }, + ], + meta: { + type: 'esaggs', + source: 'security-solution-default', + statistics: { + totalCount: 2, + }, + }, + }, + }, + ], +}; + +export const mockOnSingleValueFilterCallbackEventData: { data: ClickTriggerEventData['data'] } = { + data: [ + { + row: 0, + column: 1, + table: { + columns: [ + { + id: 'breakdown_column', + name: 'Top 3 values of event.module', + meta: { + type: 'string', + field: 'event.module', + index: + '.alerts-security.alerts-default,apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,traces-apm*,winlogbeat-*,-*elastic-cloud-logs-*', + params: { + id: 'terms', + params: { + id: 'string', + otherBucketLabel: 'Other', + missingBucketLabel: '(missing value)', + }, + }, + source: 'esaggs', + sourceParams: { + hasPrecisionError: false, + indexPatternId: 'security-solution-default', + id: '0', + enabled: true, + type: 'terms', + params: { + field: 'event.module', + orderBy: '2', + order: 'desc', + size: 3, + otherBucket: true, + otherBucketLabel: 'Other', + missingBucket: false, + missingBucketLabel: '(missing value)', + includeIsRegex: false, + excludeIsRegex: false, + }, + schema: 'segment', + }, + }, + }, + { + id: 'date_column', + name: '@timestamp per 30 seconds', + meta: { + type: 'date', + field: '@timestamp', + index: + '.alerts-security.alerts-default,apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,traces-apm*,winlogbeat-*,-*elastic-cloud-logs-*', + params: { + id: 'date', + params: { + pattern: 'HH:mm:ss', + }, + }, + source: 'esaggs', + sourceParams: { + hasPrecisionError: false, + indexPatternId: 'security-solution-default', + appliedTimeRange: { + from: '2023-08-17T09:25:24.869Z', + to: '2023-08-17T09:40:24.869Z', + }, + id: '1', + enabled: true, + type: 'date_histogram', + params: { + field: '@timestamp', + timeRange: { + from: '2023-08-17T09:25:24.869Z', + to: '2023-08-17T09:40:24.869Z', + }, + useNormalizedEsInterval: true, + extendToTimeRange: false, + scaleMetricValues: false, + interval: 'auto', + used_interval: '30s', + drop_partials: false, + min_doc_count: 1, + extended_bounds: {}, + }, + schema: 'segment', + }, + }, + }, + { + id: 'count_column', + name: 'Count of records', + meta: { + type: 'number', + index: + '.alerts-security.alerts-default,apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,traces-apm*,winlogbeat-*,-*elastic-cloud-logs-*', + params: { + id: 'number', + params: { + pattern: '0,0', + formatOverride: true, + }, + }, + source: 'esaggs', + sourceParams: { + hasPrecisionError: false, + indexPatternId: 'security-solution-default', + id: '2', + enabled: true, + type: 'count', + params: { + emptyAsNull: false, + }, + schema: 'metric', + }, + }, + }, + ], + rows: [ + { + breakdown_column: 'endpoint', + date_column: 1692264810000, + count_column: 2, + }, + ], + }, + value: 1692264810000, + }, + { + row: 0, + column: 0, + value: 'endpoint', + table: { + columns: [ + { + id: 'breakdown_column', + name: 'Top 3 values of event.module', + meta: { + type: 'string', + field: 'event.module', + index: + '.alerts-security.alerts-default,apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,traces-apm*,winlogbeat-*,-*elastic-cloud-logs-*', + params: { + id: 'terms', + params: { + id: 'string', + otherBucketLabel: 'Other', + missingBucketLabel: '(missing value)', + }, + }, + source: 'esaggs', + sourceParams: { + hasPrecisionError: false, + indexPatternId: 'security-solution-default', + id: '0', + enabled: true, + type: 'terms', + params: { + field: 'event.module', + orderBy: '2', + order: 'desc', + size: 3, + otherBucket: true, + otherBucketLabel: 'Other', + missingBucket: false, + missingBucketLabel: '(missing value)', + includeIsRegex: false, + excludeIsRegex: false, + }, + schema: 'segment', + }, + }, + }, + { + id: 'date_column', + name: '@timestamp per 30 seconds', + meta: { + type: 'date', + field: '@timestamp', + index: + '.alerts-security.alerts-default,apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,traces-apm*,winlogbeat-*,-*elastic-cloud-logs-*', + params: { + id: 'date', + params: { + pattern: 'HH:mm:ss', + }, + }, + source: 'esaggs', + sourceParams: { + hasPrecisionError: false, + indexPatternId: 'security-solution-default', + appliedTimeRange: { + from: '2023-08-17T09:25:24.869Z', + to: '2023-08-17T09:40:24.869Z', + }, + id: '1', + enabled: true, + type: 'date_histogram', + params: { + field: '@timestamp', + timeRange: { + from: '2023-08-17T09:25:24.869Z', + to: '2023-08-17T09:40:24.869Z', + }, + useNormalizedEsInterval: true, + extendToTimeRange: false, + scaleMetricValues: false, + interval: 'auto', + used_interval: '30s', + drop_partials: false, + min_doc_count: 1, + extended_bounds: {}, + }, + schema: 'segment', + }, + }, + }, + { + id: 'count_column', + name: 'Count of records', + meta: { + type: 'number', + index: + '.alerts-security.alerts-default,apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,traces-apm*,winlogbeat-*,-*elastic-cloud-logs-*', + params: { + id: 'number', + params: { + pattern: '0,0', + formatOverride: true, + }, + }, + source: 'esaggs', + sourceParams: { + hasPrecisionError: false, + indexPatternId: 'security-solution-default', + id: '2', + enabled: true, + type: 'count', + params: { + emptyAsNull: false, + }, + schema: 'metric', + }, + }, + }, + ], + rows: [ + { + breakdown_column: 'endpoint', + date_column: 1692264810000, + count_column: 2, + }, + ], + }, + }, + ], +}; + +export const mockBrushEndCallbackEventData: BrushTriggerEvent['data'] = { + range: [1688279924909, 1692234058529], + table: { + type: 'datatable', + columns: [ + { + id: 'breakdown_column', + name: 'Top 3 values of event.module', + meta: { + type: 'string', + field: 'event.module', + index: + '.alerts-security.alerts-default,apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,traces-apm*,winlogbeat-*,-*elastic-cloud-logs-*', + params: { + id: 'terms', + params: { + id: 'string', + otherBucketLabel: 'Other', + missingBucketLabel: '(missing value)', + }, + }, + source: 'esaggs', + sourceParams: { + hasPrecisionError: false, + indexPatternId: 'security-solution-default', + id: '0', + enabled: true, + type: 'terms', + params: { + field: 'event.module', + orderBy: '2', + order: 'desc', + size: 3, + otherBucket: true, + otherBucketLabel: 'Other', + missingBucket: false, + missingBucketLabel: '(missing value)', + includeIsRegex: false, + excludeIsRegex: false, + }, + schema: 'segment', + }, + }, + }, + { + id: 'date_column', + name: '@timestamp per 7 days', + meta: { + type: 'date', + field: '@timestamp', + index: + '.alerts-security.alerts-default,apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,traces-apm*,winlogbeat-*,-*elastic-cloud-logs-*', + params: { + id: 'date', + params: { + pattern: 'YYYY-MM-DD', + }, + }, + source: 'esaggs', + sourceParams: { + hasPrecisionError: false, + indexPatternId: 'security-solution-default', + appliedTimeRange: { + from: '2022-05-17T14:22:09.039Z', + to: '2023-08-17T14:22:09.039Z', + }, + id: '1', + enabled: true, + type: 'date_histogram', + params: { + field: '@timestamp', + timeRange: { + from: '2022-05-17T14:22:09.039Z', + to: '2023-08-17T14:22:09.039Z', + }, + useNormalizedEsInterval: true, + extendToTimeRange: false, + scaleMetricValues: false, + interval: 'auto', + used_interval: '1w', + drop_partials: false, + min_doc_count: 1, + extended_bounds: {}, + }, + schema: 'segment', + }, + }, + }, + { + id: 'count_column', + name: 'Count of records', + meta: { + type: 'number', + index: + '.alerts-security.alerts-default,apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,traces-apm*,winlogbeat-*,-*elastic-cloud-logs-*', + params: { + id: 'number', + params: { + pattern: '0,0', + formatOverride: true, + }, + }, + source: 'esaggs', + sourceParams: { + hasPrecisionError: false, + indexPatternId: 'security-solution-default', + id: '2', + enabled: true, + type: 'count', + params: { + emptyAsNull: false, + }, + schema: 'metric', + }, + }, + }, + ], + rows: [ + { + breakdown_column: 'endpoint', + date_column: 1691964000000, + count_column: 2, + }, + ], + meta: { + type: 'esaggs', + source: 'security-solution-default', + statistics: { + totalCount: 27, + }, + }, + }, + column: 1, +}; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/customizations/use_histogram_customizations.test.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/customizations/use_histogram_customizations.test.ts new file mode 100644 index 0000000000000..8e5960dde468f --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/customizations/use_histogram_customizations.test.ts @@ -0,0 +1,195 @@ +/* + * 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 { TestProviders } from '../../../../../common/mock'; +import type { + BrushTriggerEvent, + ClickTriggerEvent, + MultiClickTriggerEvent, +} from '@kbn/charts-plugin/public'; +import { renderHook } from '@testing-library/react-hooks'; +import type { + DiscoverStateContainer, + UnifiedHistogramCustomization, + DiscoverCustomization, +} from '@kbn/discover-plugin/public'; +import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; +import type { WithPreventableEvent } from './use_histogram_customizations'; +import { useHistogramCustomization } from './use_histogram_customizations'; +import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; +import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks'; +import { createStartServicesMock } from '../../../../../common/lib/kibana/kibana_react.mock'; +import { + mockBrushEndCallbackEventData, + mockOnMultiValueFilterCallbackEventData, + mockOnSingleValueFilterCallbackEventData, +} from './mock.data'; +import { + getEventDataWithPreventableEvent, + getMockCustomizationWithCustomSetFunction, +} from '../utils/test_utils'; +import { useKibana } from '../../../../../common/lib/kibana'; +import { mockApplyFilterTrigger, mockPreventDefault, mockUIActionsGetTrigger } from '../mocks'; + +const mockDataService = dataPluginMock.createStartContract(); + +const mockUIActions = { + ...uiActionsPluginMock.createStartContract(), + getTrigger: mockUIActionsGetTrigger, +} as UiActionsStart; + +jest.mock('../../../../../common/lib/kibana'); + +const renderHookWithContext = () => { + return renderHook(() => useHistogramCustomization(), { + wrapper: TestProviders, + }); +}; + +const mockSetFunctionOnFilterCallbackWithSingleValueFilter = ( + histogramCustomization: DiscoverCustomization +) => { + const { onFilter } = histogramCustomization as UnifiedHistogramCustomization; + onFilter?.( + getEventDataWithPreventableEvent( + mockOnSingleValueFilterCallbackEventData as ClickTriggerEvent['data'] + ) + ); +}; + +const mockSetFunctionOnFilterCallbackWithMultiValueFilter = ( + histogramCustomization: DiscoverCustomization +) => { + const { onFilter } = histogramCustomization as UnifiedHistogramCustomization; + onFilter?.( + getEventDataWithPreventableEvent( + mockOnMultiValueFilterCallbackEventData as MultiClickTriggerEvent['data'] + ) + ); +}; + +const mockSetFunctionOnBrushEndCallback = (histogramCustomization: DiscoverCustomization) => { + const { onBrushEnd } = histogramCustomization as UnifiedHistogramCustomization; + onBrushEnd?.( + getEventDataWithPreventableEvent(mockBrushEndCallbackEventData) as WithPreventableEvent< + BrushTriggerEvent['data'] + > + ); +}; + +const mockStateContainer = {} as DiscoverStateContainer; + +describe('useHistogramCustomization', () => { + const startServices = createStartServicesMock(); + beforeAll(() => { + (useKibana as jest.Mock).mockReturnValue({ + services: { + ...startServices, + customDataService: mockDataService, + uiActions: mockUIActions, + }, + }); + }); + describe('onFilterCallback', () => { + beforeEach(() => jest.clearAllMocks()); + it('should apply filter correctly, in case of single value click Trigger', async () => { + ( + mockDataService.actions.createFiltersFromValueClickAction as jest.Mock + ).mockResolvedValueOnce('some_filter'); + + const renderHookResult = renderHookWithContext(); + + const setHistogramCustomizationCallback = renderHookResult.result.current; + + const mockCustomization = getMockCustomizationWithCustomSetFunction( + mockSetFunctionOnFilterCallbackWithSingleValueFilter + ); + + const callHistogramCustomization = async () => { + setHistogramCustomizationCallback({ + customizations: mockCustomization, + stateContainer: mockStateContainer, + }); + }; + + await callHistogramCustomization(); + + expect(mockDataService.actions.createFiltersFromValueClickAction).toHaveBeenNthCalledWith( + 1, + expect.objectContaining(mockOnSingleValueFilterCallbackEventData) + ); + + expect(mockPreventDefault).toHaveBeenCalledTimes(1); + + expect(mockApplyFilterTrigger.exec).toHaveBeenCalledWith({ + filters: ['some_filter'], + }); + }); + + it('should apply filter correctly, in case of multi value click Trigger', async () => { + ( + mockDataService.actions.createFiltersFromMultiValueClickAction as jest.Mock + ).mockResolvedValueOnce(['some_filter']); + + const renderHookResult = renderHookWithContext(); + + const setHistogramCustomizationCallback = renderHookResult.result.current; + + const mockCustomization = getMockCustomizationWithCustomSetFunction( + mockSetFunctionOnFilterCallbackWithMultiValueFilter + ); + + const callHistogramCustomization = async () => { + setHistogramCustomizationCallback({ + customizations: mockCustomization, + stateContainer: mockStateContainer, + }); + }; + + await callHistogramCustomization(); + + expect( + mockDataService.actions.createFiltersFromMultiValueClickAction + ).toHaveBeenNthCalledWith(1, mockOnMultiValueFilterCallbackEventData); + expect(mockPreventDefault).toHaveBeenCalledTimes(1); + + expect(mockApplyFilterTrigger.exec).toHaveBeenCalledWith({ + filters: ['some_filter'], + }); + }); + }); + + describe('onBrushEndCallback', () => { + beforeEach(() => jest.clearAllMocks()); + it('should apply timerange in correctly in case of brush end event', async () => { + const renderHookResult = renderHookWithContext(); + + const setHistogramCustomizationCallback = renderHookResult.result.current; + + const mockCustomization = getMockCustomizationWithCustomSetFunction( + mockSetFunctionOnBrushEndCallback + ); + + const callHistogramCustomization = async () => { + setHistogramCustomizationCallback({ + customizations: mockCustomization, + stateContainer: mockStateContainer, + }); + }; + + await callHistogramCustomization(); + + expect(mockDataService.query.timefilter.timefilter.setTime).toHaveBeenNthCalledWith(1, { + from: '2023-07-02T06:38:44.909Z', + mode: 'absolute', + to: '2023-08-17T01:00:58.529Z', + }); + + expect(mockPreventDefault).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/customizations/use_histogram_customizations.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/customizations/use_histogram_customizations.tsx new file mode 100644 index 0000000000000..9ff69adbf4655 --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/customizations/use_histogram_customizations.tsx @@ -0,0 +1,109 @@ +/* + * 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 type { + BrushTriggerEvent, + ClickTriggerEvent, + MultiClickTriggerEvent, +} from '@kbn/charts-plugin/public'; +import type { CustomizationCallback } from '@kbn/discover-plugin/public'; +import type { UnifiedHistogramContainerProps } from '@kbn/unified-histogram-plugin/public'; +import { ACTION_GLOBAL_APPLY_FILTER } from '@kbn/unified-search-plugin/public'; +import { useCallback } from 'react'; +import { DiscoverInTimelineTrigger } from '../../../../../actions/constants'; +import { useKibana } from '../../../../../common/lib/kibana'; + +export type WithPreventableEvent = T & { + preventDefault(): void; +}; + +export type ClickTriggerEventData = ClickTriggerEvent['data'] | MultiClickTriggerEvent['data']; + +type CustomClickTriggerEvent = WithPreventableEvent; + +const isClickTriggerEvent = ( + e: CustomClickTriggerEvent +): e is WithPreventableEvent => { + return Array.isArray(e.data) && 'column' in e.data[0]; +}; + +const isMultiValueTriggerEvent = ( + e: CustomClickTriggerEvent +): e is WithPreventableEvent => { + return Array.isArray(e.data) && 'cells' in e.data[0]; +}; + +export const useHistogramCustomization = () => { + const { + services: { customDataService: discoverDataService, uiActions }, + } = useKibana(); + + const onFilterCallback: UnifiedHistogramContainerProps['onFilter'] = useCallback( + async (eventData: WithPreventableEvent) => { + if (eventData.preventDefault) eventData.preventDefault(); + let filters; + + if (isClickTriggerEvent(eventData)) { + filters = await discoverDataService.actions.createFiltersFromValueClickAction({ + data: eventData.data, + negate: eventData.negate ?? false, + }); + } else if (isMultiValueTriggerEvent(eventData)) { + filters = await discoverDataService.actions.createFiltersFromMultiValueClickAction({ + data: eventData.data, + negate: eventData.negate, + }); + } else { + // no-op + return; + } + + if (filters && !Array.isArray(filters)) { + filters = [filters]; + } + + if (filters && filters.length > 0) { + const applyFilterTrigger = uiActions.getTrigger( + DiscoverInTimelineTrigger.HISTOGRAM_TRIGGER + ); + + await applyFilterTrigger.exec({ + filters, + timeFieldName: eventData.timeFieldName, + }); + } + }, + [uiActions, discoverDataService.actions] + ); + + const onBrushEndCallback: UnifiedHistogramContainerProps['onBrushEnd'] = useCallback( + (data: WithPreventableEvent) => { + discoverDataService.query.timefilter.timefilter.setTime({ + from: new Date(data.range[0]).toISOString(), + to: new Date(data.range[1]).toISOString(), + mode: 'absolute', + }); + if (data.preventDefault) data.preventDefault(); + }, + [discoverDataService.query.timefilter.timefilter] + ); + + const setHistogramCustomizationCallback: CustomizationCallback = useCallback( + ({ customizations }) => { + customizations.set({ + id: 'unified_histogram', + onFilter: onFilterCallback, + onBrushEnd: onBrushEndCallback, + withDefaultActions: false, + disabledActions: [ACTION_GLOBAL_APPLY_FILTER], + }); + }, + [onFilterCallback, onBrushEndCallback] + ); + + return setHistogramCustomizationCallback; +}; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/customizations/use_search_bar_customizations.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/customizations/use_search_bar_customizations.tsx new file mode 100644 index 0000000000000..66ef304dc7b7f --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/customizations/use_search_bar_customizations.tsx @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { CustomizationCallback } from '@kbn/discover-plugin/public'; +import { useGetStatefulQueryBar } from '../use_get_stateful_query_bar'; + +export const useSearchBarCustomizations = () => { + const { CustomStatefulTopNavKqlQueryBar } = useGetStatefulQueryBar(); + + const setSearchBarCustomizations: CustomizationCallback = ({ customizations }) => { + customizations.set({ + id: 'search_bar', + CustomSearchBar: CustomStatefulTopNavKqlQueryBar, + }); + }; + + return setSearchBarCustomizations; +}; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/customizations/use_set_discover_customizations.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/customizations/use_set_discover_customizations.ts new file mode 100644 index 0000000000000..3ae7f5c86ad00 --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/customizations/use_set_discover_customizations.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { CustomizationCallback } from '@kbn/discover-plugin/public/customizations/types'; +import { useHistogramCustomization } from './use_histogram_customizations'; +import { useSearchBarCustomizations } from './use_search_bar_customizations'; + +export const useSetDiscoverCustomizationCallbacks = (): CustomizationCallback[] => { + const searchBarCustomizationCallback = useSearchBarCustomizations(); + + const histogramCustomizationCallback = useHistogramCustomization(); + + return [searchBarCustomizationCallback, histogramCustomizationCallback]; +}; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/index.tsx index d853baefbbd8e..deb52a77a5012 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/index.tsx @@ -5,13 +5,20 @@ * 2.0. */ -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useHistory } from 'react-router-dom'; import type { CustomizationCallback } from '@kbn/discover-plugin/public/customizations/types'; -import styled, { createGlobalStyle } from 'styled-components'; +import { createGlobalStyle } from 'styled-components'; import type { ScopedHistory } from '@kbn/core/public'; +import type { DiscoverStateContainer } from '@kbn/discover-plugin/public'; +import type { Subscription } from 'rxjs'; +import type { DataView } from '@kbn/data-views-plugin/common'; +import { useSourcererDataView } from '../../../../common/containers/sourcerer'; import { useKibana } from '../../../../common/lib/kibana'; -import { useGetStatefulQueryBar } from './use_get_stateful_query_bar'; +import { useDiscoverState } from './use_discover_state'; +import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; +import { useSetDiscoverCustomizationCallbacks } from './customizations/use_set_discover_customizations'; +import { EmbeddedDiscoverContainer } from './styles'; const HideSearchSessionIndicatorBreadcrumbIcon = createGlobalStyle` [data-test-subj='searchSessionIndicator'] { @@ -19,49 +26,121 @@ const HideSearchSessionIndicatorBreadcrumbIcon = createGlobalStyle` } `; -const EmbeddedDiscoverContainer = styled.div` - width: 100%; - height: 100%; - overflow: scroll; - display: grid, - place-items: center -`; - export const DiscoverTabContent = () => { const history = useHistory(); const { - services: { customDataService: discoverDataService, discover, discoverFilterManager }, + services: { customDataService: discoverDataService, discover, dataViews: dataViewService }, } = useKibana(); - const { CustomStatefulTopNavKqlQueryBar } = useGetStatefulQueryBar(); + const { dataViewId } = useSourcererDataView(SourcererScopeName.detections); + + const [dataView, setDataView] = useState(); + + const stateContainerRef = useRef(); + + const discoverAppStateSubscription = useRef(); + const discoverInternalStateSubscription = useRef(); + const discoverSavedSearchStateSubscription = useRef(); + + const discoverCustomizationCallbacks = useSetDiscoverCustomizationCallbacks(); + + const { + discoverAppState, + discoverInternalState, + discoverSavedSearchState, + setDiscoverSavedSearchState, + setDiscoverInternalState, + setDiscoverAppState, + } = useDiscoverState(); + + useEffect(() => { + if (!dataViewId) return; + dataViewService.get(dataViewId).then(setDataView); + }, [dataViewId, dataViewService]); + + useEffect(() => { + const unSubscribeAll = () => { + [ + discoverAppStateSubscription.current, + discoverInternalStateSubscription.current, + discoverSavedSearchStateSubscription.current, + ].forEach((sub) => { + if (sub) sub.unsubscribe(); + }); + }; + + return unSubscribeAll; + }, []); + + const initialDiscoverCustomizationCallback: CustomizationCallback = useCallback( + async ({ stateContainer }) => { + stateContainerRef.current = stateContainer; + + if (discoverAppState && discoverInternalState && discoverSavedSearchState) { + stateContainer.appState.set(discoverAppState); + await stateContainer.appState.replaceUrlState(discoverAppState); + } else { + // set initial dataView Id + if (dataView) stateContainer.actions.setDataView(dataView); + } - const customize: CustomizationCallback = useCallback( - ({ customizations }) => { - customizations.set({ - id: 'search_bar', - CustomSearchBar: CustomStatefulTopNavKqlQueryBar, + const unsubscribeState = stateContainer.appState.state$.subscribe({ + next: setDiscoverAppState, }); + + const internalStateSubscription = stateContainer.internalState.state$.subscribe({ + next: setDiscoverInternalState, + }); + + const savedSearchStateSub = stateContainer.savedSearchState.getHasChanged$().subscribe({ + next: (hasChanged) => { + if (hasChanged) { + const latestSavedSearchState = stateContainer.savedSearchState.getState(); + setDiscoverSavedSearchState(latestSavedSearchState); + } + }, + }); + + discoverAppStateSubscription.current = unsubscribeState; + discoverInternalStateSubscription.current = internalStateSubscription; + discoverSavedSearchStateSubscription.current = savedSearchStateSub; }, - [CustomStatefulTopNavKqlQueryBar] + [ + discoverAppState, + discoverInternalState, + discoverSavedSearchState, + setDiscoverSavedSearchState, + setDiscoverInternalState, + setDiscoverAppState, + dataView, + ] + ); + + const customizationsCallbacks = useMemo( + () => [initialDiscoverCustomizationCallback, ...discoverCustomizationCallbacks], + [initialDiscoverCustomizationCallback, discoverCustomizationCallbacks] ); const services = useMemo( () => ({ - filterManager: discoverFilterManager, data: discoverDataService, + filterManager: discoverDataService.query.filterManager, }), - [discoverDataService, discoverFilterManager] + [discoverDataService] ); const DiscoverContainer = discover.DiscoverContainer; + const isLoading = !dataView; + return ( ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/mocks/discover_tab_content.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/mocks/discover_tab_content.tsx new file mode 100644 index 0000000000000..d49c197250bc3 --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/mocks/discover_tab_content.tsx @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EmbeddedDiscoverContainer } from '../styles'; + +export function MockDiscoverTabContent() { + return ( + + {'Mock Discover Tab Content'} + + ); +} diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/mocks/index.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/mocks/index.ts new file mode 100644 index 0000000000000..b77dbfd0d1e79 --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/mocks/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DiscoverInTimelineTrigger } from '../../../../../actions/constants'; +export { MockDiscoverTabContent } from './discover_tab_content'; + +export const mockApplyFilterTrigger = { + exec: jest.fn().mockResolvedValue(undefined), +}; + +export const mockPreventDefault = jest.fn(); + +export const mockUIActionsGetTrigger = jest.fn().mockImplementation((triggerName: string) => { + switch (triggerName) { + case DiscoverInTimelineTrigger.HISTOGRAM_TRIGGER: + return mockApplyFilterTrigger; + default: + return undefined; + } +}); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/styles.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/styles.tsx new file mode 100644 index 0000000000000..e5dab5e5a74ea --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/styles.tsx @@ -0,0 +1,16 @@ +/* + * 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 styled from '@emotion/styled'; + +export const EmbeddedDiscoverContainer = styled.div` + width: 100%; + height: 100%; + overflow: scroll; + display: grid, + place-items: center +`; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/use_discover_state.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/use_discover_state.ts new file mode 100644 index 0000000000000..7ff5f20dfb11d --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/use_discover_state.ts @@ -0,0 +1,65 @@ +/* + * 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 type { DiscoverAppState } from '@kbn/discover-plugin/public/application/main/services/discover_app_state_container'; +import type { InternalState } from '@kbn/discover-plugin/public/application/main/services/discover_internal_state_container'; +import type { SavedSearch } from '@kbn/saved-search-plugin/common'; +import { useCallback } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { + updateDiscoverAppState, + updateDiscoverInternalState, + updateDiscoverSavedSearchState, +} from '../../../../common/store/discover/actions'; +import type { State } from '../../../../common/store'; + +export const useDiscoverState = () => { + const discoverAppState = useSelector((state) => { + const result = state.discover.app; + return result; + }); + const discoverInternalState = useSelector((state) => { + const result = state.discover.internal; + return result; + }); + const discoverSavedSearchState = useSelector((state) => { + const result = state.discover.savedSearch; + return result; + }); + + const dispatch = useDispatch(); + + const setDiscoverAppState = useCallback( + (newState: DiscoverAppState) => { + dispatch(updateDiscoverAppState({ newState })); + }, + [dispatch] + ); + + const setDiscoverInternalState = useCallback( + (newState: InternalState) => { + dispatch(updateDiscoverInternalState({ newState })); + }, + [dispatch] + ); + + const setDiscoverSavedSearchState = useCallback( + (newState: SavedSearch) => { + dispatch(updateDiscoverSavedSearchState({ newState })); + }, + [dispatch] + ); + + return { + discoverAppState, + setDiscoverAppState, + discoverInternalState, + setDiscoverInternalState, + discoverSavedSearchState, + setDiscoverSavedSearchState, + }; +}; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/utils/test_utils.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/utils/test_utils.ts new file mode 100644 index 0000000000000..1de05599fa6dd --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/utils/test_utils.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { BrushTriggerEvent } from '@kbn/charts-plugin/public'; +import type { + DiscoverCustomization, + DiscoverCustomizationService, +} from '@kbn/discover-plugin/public/customizations/customization_service'; +import type { + ClickTriggerEventData, + WithPreventableEvent, +} from '../customizations/use_histogram_customizations'; +import { mockPreventDefault } from '../mocks'; + +type CustomizationSetFunction = (customization: DiscoverCustomization) => void; + +export const getMockCustomizationWithCustomSetFunction = ( + mockSetFunction: CustomizationSetFunction +): DiscoverCustomizationService => { + const mockCustomization = { + set: mockSetFunction, + } as DiscoverCustomizationService; + + return mockCustomization; +}; + +export const getEventDataWithPreventableEvent = < + T extends ClickTriggerEventData | BrushTriggerEvent['data'] +>( + eventData: T +): WithPreventableEvent => ({ + ...eventData, + preventDefault: mockPreventDefault, +}); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx index 50d0c9b5c6277..3d5b1a95d66da 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx @@ -43,6 +43,7 @@ import { import * as i18n from './translations'; import { useLicense } from '../../../../common/hooks/use_license'; import { TIMELINE_CONVERSATION_TITLE } from '../../../../assistant/content/conversations/translations'; +import { initializeTimelineSettings } from '../../../store/timeline/actions'; const HideShowContainer = styled.div.attrs<{ $isVisible: boolean; isOverflowYScroll: boolean }>( ({ $isVisible = false, isOverflowYScroll = false }) => ({ @@ -377,8 +378,13 @@ const TabsContentComponent: React.FC = ({ }, [activeTab, conversationId, reportAssistantInvoked, setActiveTab]); const setDiscoverAsActiveTab = useCallback(() => { + dispatch( + initializeTimelineSettings({ + id: timelineId, + }) + ); setActiveTab(TimelineTabs.discover); - }, [setActiveTab]); + }, [setActiveTab, dispatch, timelineId]); useEffect(() => { if (!graphEventId && activeTab === TimelineTabs.graph) { diff --git a/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx index c07e52b09874f..3b326e492d132 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx @@ -11,7 +11,7 @@ import ReactDOM from 'react-dom'; import deepEqual from 'fast-deep-equal'; import { Subscription } from 'rxjs'; -import { isCompleteResponse, isErrorResponse } from '@kbn/data-plugin/common'; +import { isCompleteResponse } from '@kbn/data-plugin/common'; import { EntityType } from '@kbn/timelines-plugin/common'; import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; import { useKibana } from '../../../common/lib/kibana'; @@ -62,7 +62,7 @@ export const useTimelineEventsDetails = ({ const [loading, setLoading] = useState(true); const [timelineDetailsRequest, setTimelineDetailsRequest] = useState(null); - const { addError, addWarning } = useAppToasts(); + const { addError } = useAppToasts(); const [timelineDetailsResponse, setTimelineDetailsResponse] = useState(null); @@ -99,10 +99,6 @@ export const useTimelineEventsDetails = ({ searchSubscription$.current.unsubscribe(); }); }); - } else if (isErrorResponse(response)) { - setLoading(false); - addWarning(i18n.FAIL_TIMELINE_DETAILS); - searchSubscription$.current.unsubscribe(); } }, error: (msg) => { @@ -117,7 +113,7 @@ export const useTimelineEventsDetails = ({ asyncSearch(); refetch.current = asyncSearch; }, - [data.search, addError, addWarning, skip] + [data.search, addError, skip] ); useEffect(() => { diff --git a/x-pack/plugins/security_solution/public/timelines/containers/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/index.tsx index 6d2837db0eddc..b65c7c7c51498 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/index.tsx @@ -12,7 +12,7 @@ import { useDispatch } from 'react-redux'; import { Subscription } from 'rxjs'; import type { DataView } from '@kbn/data-plugin/common'; -import { isCompleteResponse, isErrorResponse } from '@kbn/data-plugin/common'; +import { isCompleteResponse } from '@kbn/data-plugin/common'; import type { ESQuery } from '../../../common/typed_json'; import type { inputsModel } from '../../common/store'; @@ -32,7 +32,6 @@ import type { } from '../../../common/search_strategy'; import { Direction, TimelineEventsQueries } from '../../../common/search_strategy'; import type { InspectResponse } from '../../types'; -import * as i18n from './translations'; import type { KueryFilterQueryKind } from '../../../common/types/timeline'; import { TimelineId } from '../../../common/types/timeline'; import { useRouteSpy } from '../../common/utils/route/use_route_spy'; @@ -42,7 +41,6 @@ import type { TimelineEqlRequestOptions, TimelineEqlResponse, } from '../../../common/search_strategy/timeline/events/eql'; -import { useAppToasts } from '../../common/hooks/use_app_toasts'; import { useTrackHttpRequest } from '../../common/lib/apm/use_track_http_request'; import { APP_UI_ID } from '../../../common/constants'; @@ -221,8 +219,6 @@ export const useTimelineEventsHandler = ({ } }, [setUpdated, timelineResponse.updatedAt]); - const { addWarning } = useAppToasts(); - const timelineSearch = useCallback( async ( request: TimelineRequest | null, @@ -275,11 +271,6 @@ export const useTimelineEventsHandler = ({ return newTimelineResponse; }); - searchSubscription$.current.unsubscribe(); - } else if (isErrorResponse(response)) { - endTracking('invalid'); - setLoading(false); - addWarning(i18n.ERROR_TIMELINE_EVENTS); searchSubscription$.current.unsubscribe(); } }, @@ -335,17 +326,7 @@ export const useTimelineEventsHandler = ({ await asyncSearch(); refetch.current = asyncSearch; }, - [ - pageName, - skip, - id, - startTracking, - data.search, - dataViewId, - addWarning, - refetchGrid, - wrappedLoadPage, - ] + [pageName, skip, id, startTracking, data.search, dataViewId, refetchGrid, wrappedLoadPage] ); useEffect(() => { diff --git a/x-pack/plugins/security_solution/public/timelines/containers/kpis/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/kpis/index.tsx index 5bd71f4f7be94..f2a1d81ec0294 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/kpis/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/kpis/index.tsx @@ -10,7 +10,7 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import deepEqual from 'fast-deep-equal'; import { Subscription } from 'rxjs'; -import { isCompleteResponse, isErrorResponse } from '@kbn/data-plugin/public'; +import { isCompleteResponse } from '@kbn/data-plugin/public'; import type { inputsModel } from '../../../common/store'; import { useKibana } from '../../../common/lib/kibana'; import type { @@ -46,7 +46,7 @@ export const useTimelineKpis = ({ ); const [timelineKpiResponse, setTimelineKpiResponse] = useState(null); - const { addError, addWarning } = useAppToasts(); + const { addError } = useAppToasts(); const timelineKpiSearch = useCallback( (request: TimelineKpiStrategyRequest | null) => { @@ -68,10 +68,6 @@ export const useTimelineKpis = ({ setLoading(false); setTimelineKpiResponse(response); searchSubscription$.current.unsubscribe(); - } else if (isErrorResponse(response)) { - setLoading(false); - addWarning(i18n.FAIL_TIMELINE_KPI_DETAILS); - searchSubscription$.current.unsubscribe(); } }, error: (msg) => { @@ -86,7 +82,7 @@ export const useTimelineKpis = ({ asyncSearch(); refetch.current = asyncSearch; }, - [data.search, addError, addWarning] + [data.search, addError] ); useEffect(() => { diff --git a/x-pack/plugins/security_solution/public/timelines/links.ts b/x-pack/plugins/security_solution/public/timelines/links.ts index bd3b48164d097..64dbded0822f5 100644 --- a/x-pack/plugins/security_solution/public/timelines/links.ts +++ b/x-pack/plugins/security_solution/public/timelines/links.ts @@ -14,7 +14,7 @@ export const links: LinkItem = { id: SecurityPageName.timelines, title: TIMELINES, path: TIMELINES_PATH, - globalNavPosition: 5, + globalNavPosition: 6, capabilities: [`${SERVER_APP_ID}.show`], globalSearchKeywords: [ i18n.translate('xpack.securitySolution.appLinks.timelines', { diff --git a/x-pack/plugins/security_solution/public/types.ts b/x-pack/plugins/security_solution/public/types.ts index e958e22019c54..b1fc18359f4f6 100644 --- a/x-pack/plugins/security_solution/public/types.ts +++ b/x-pack/plugins/security_solution/public/types.ts @@ -9,7 +9,7 @@ import type { Observable } from 'rxjs'; import type { AppLeaveHandler, CoreStart } from '@kbn/core/public'; import type { HomePublicPluginSetup } from '@kbn/home-plugin/public'; -import type { DataPublicPluginStart, FilterManager } from '@kbn/data-plugin/public'; +import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { FieldFormatsStartCommon } from '@kbn/field-formats-plugin/common'; import type { EmbeddableStart } from '@kbn/embeddable-plugin/public'; import type { LensPublicStart } from '@kbn/lens-plugin/public'; @@ -163,7 +163,6 @@ export type StartServices = CoreStart & }; savedObjectsManagement: SavedObjectsManagementPluginStart; telemetry: TelemetryClientStart; - discoverFilterManager: FilterManager; customDataService: DataPublicPluginStart; topValuesPopover: TopValuesPopoverService; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/utils.ts index 003e55fa6f1b8..819bf87165e12 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/utils.ts @@ -100,5 +100,5 @@ export const getOutputRuleAlertForRest = (): RuleResponse => ({ namespace: undefined, data_view_id: undefined, alert_suppression: undefined, - investigation_fields: [], + investigation_fields: undefined, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/schedule_notification_actions.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/schedule_notification_actions.test.ts index 18f6bf5f21fc8..1d6d0174dae75 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/schedule_notification_actions.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/schedule_notification_actions.test.ts @@ -44,7 +44,7 @@ describe('schedule_notification_actions', () => { responseActions: [], riskScore: 80, riskScoreMapping: [], - investigationFields: [], + investigationFields: undefined, ruleNameOverride: undefined, dataViewId: undefined, outputIndex: 'output-1', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/register_routes.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/register_routes.ts index f7c865605dc3d..ed0f19c7015b6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/register_routes.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/register_routes.ts @@ -62,8 +62,6 @@ export const registerRuleManagementRoutes = ( // Rules filters getRuleManagementFilters(router); - // Rules dashboard - if (config.experimentalFeatures.detectionsCoverageOverview) { - getCoverageOverviewRoute(router); - } + // Rules coverage overview + getCoverageOverviewRoute(router); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts index 5248e7f06938f..aa9d7c4f8303b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts @@ -45,7 +45,7 @@ export const updateRules = async ({ ruleId: existingRule.params.ruleId, falsePositives: ruleUpdate.false_positives ?? [], from: ruleUpdate.from ?? 'now-6m', - investigationFields: ruleUpdate.investigation_fields ?? [], + investigationFields: ruleUpdate.investigation_fields, // Unlike the create route, immutable comes from the existing rule here immutable: existingRule.params.immutable, license: ruleUpdate.license, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts index 662085c95d62b..6897fdaf0b92c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts @@ -136,7 +136,7 @@ describe('getExportAll', () => { note: '# Investigative notes', version: 1, exceptions_list: getListArrayMock(), - investigation_fields: [], + investigation_fields: undefined, }); expect(detailsJson).toEqual({ exported_exception_list_count: 1, @@ -320,7 +320,7 @@ describe('getExportAll', () => { version: 1, revision: 0, exceptions_list: getListArrayMock(), - investigation_fields: [], + investigation_fields: undefined, }); expect(detailsJson).toEqual({ exported_exception_list_count: 1, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts index b8f27c0d16a5c..c2fe91251d75d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts @@ -132,7 +132,7 @@ describe('get_export_by_object_ids', () => { note: '# Investigative notes', version: 1, exceptions_list: getListArrayMock(), - investigation_fields: [], + investigation_fields: undefined, }, exportDetails: { exported_exception_list_count: 0, @@ -328,7 +328,7 @@ describe('get_export_by_object_ids', () => { version: 1, revision: 0, exceptions_list: getListArrayMock(), - investigation_fields: [], + investigation_fields: undefined, }); expect(detailsJson).toEqual({ exported_exception_list_count: 0, @@ -525,7 +525,7 @@ describe('get_export_by_object_ids', () => { namespace: undefined, data_view_id: undefined, alert_suppression: undefined, - investigation_fields: [], + investigation_fields: undefined, }, ], }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts index 385fe889be40a..84acb597910ce 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts @@ -476,7 +476,7 @@ export const convertCreateAPIToInternalSchema = ( description: input.description, ruleId: newRuleId, falsePositives: input.false_positives ?? [], - investigationFields: input.investigation_fields ?? [], + investigationFields: input.investigation_fields, from: input.from ?? DEFAULT_FROM, immutable, license: input.license, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts index d24bcaa5a7b9b..eb442f5fc37bb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts @@ -78,7 +78,7 @@ export const ruleOutput = (): RuleResponse => ({ data_view_id: undefined, saved_id: undefined, alert_suppression: undefined, - investigation_fields: [], + investigation_fields: undefined, }); describe('validate', () => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/alert_instance_factory_stub.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/alert_instance_factory_stub.ts index df087c7d9775b..61a772ddc44d1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/alert_instance_factory_stub.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/alert_instance_factory_stub.ts @@ -29,19 +29,19 @@ export const alertInstanceFactoryStub = < replaceState(state: TInstanceState) { return new Alert('', { state: {} as TInstanceState, - meta: { lastScheduledActions: { group: 'default', date: new Date() } }, + meta: { lastScheduledActions: { group: 'default', date: new Date().toISOString() } }, }); }, scheduleActions(actionGroup: TActionGroupIds, alertcontext: TInstanceContext) { return new Alert('', { state: {} as TInstanceState, - meta: { lastScheduledActions: { group: 'default', date: new Date() } }, + meta: { lastScheduledActions: { group: 'default', date: new Date().toISOString() } }, }); }, setContext(alertContext: TInstanceContext) { return new Alert('', { state: {} as TInstanceState, - meta: { lastScheduledActions: { group: 'default', date: new Date() } }, + meta: { lastScheduledActions: { group: 'default', date: new Date().toISOString() } }, }); }, getContext() { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts index a6e6ee5282e89..0618c0ff722f1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts @@ -48,7 +48,6 @@ const getBaseRuleParams = (): BaseRuleParams => { timelineTitle: 'some-timeline-title', timestampOverride: undefined, timestampOverrideFallbackDisabled: undefined, - investigationFields: [], meta: { someMeta: 'someField', }, @@ -58,6 +57,7 @@ const getBaseRuleParams = (): BaseRuleParams => { relatedIntegrations: [], requiredFields: [], setup: '', + investigationFields: undefined, }; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts index d3e66cf49148d..449ce553bf32b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts @@ -57,7 +57,7 @@ import { RuleAuthorArray, RuleDescription, RuleFalsePositiveArray, - RuleCustomHighlightedFieldArray, + InvestigationFields, RuleFilterArray, RuleLicense, RuleMetadata, @@ -98,7 +98,7 @@ export const baseRuleParams = t.exact( falsePositives: RuleFalsePositiveArray, from: RuleIntervalFrom, ruleId: RuleSignatureId, - investigationFields: t.union([RuleCustomHighlightedFieldArray, t.undefined]), + investigationFields: t.union([InvestigationFields, t.undefined]), immutable: IsRuleImmutable, license: t.union([RuleLicense, t.undefined]), outputIndex: AlertsIndex, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.test.ts index 8e06156bdc913..a9ae0d1d55696 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.test.ts @@ -165,7 +165,7 @@ describe('buildAlert', () => { index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], query: 'user.name: root or user.name: admin', filters: [{ query: { match_phrase: { 'host.name': 'some-host' } } }], - investigation_fields: [], + investigation_fields: undefined, }, [ALERT_RULE_INDICES]: completeRule.ruleParams.index, ...flattenWithPrefix(ALERT_RULE_NAMESPACE, { @@ -359,7 +359,7 @@ describe('buildAlert', () => { index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], query: 'user.name: root or user.name: admin', filters: [{ query: { match_phrase: { 'host.name': 'some-host' } } }], - investigation_fields: [], + investigation_fields: undefined, }, ...flattenWithPrefix(ALERT_RULE_NAMESPACE, { actions: [], diff --git a/x-pack/plugins/security_solution/server/lib/risk_engine/risk_engine_data_client.ts b/x-pack/plugins/security_solution/server/lib/risk_engine/risk_engine_data_client.ts index bbbf51aef66a6..b66ec12b08d69 100644 --- a/x-pack/plugins/security_solution/server/lib/risk_engine/risk_engine_data_client.ts +++ b/x-pack/plugins/security_solution/server/lib/risk_engine/risk_engine_data_client.ts @@ -35,6 +35,7 @@ import { RiskEngineStatus, getRiskScoreLatestIndex, MAX_SPACES_COUNT, + RiskScoreEntity, } from '../../../common/risk_engine'; import { getLegacyTransforms, @@ -51,6 +52,7 @@ import { import { getRiskInputsIndex } from './get_risk_inputs_index'; import { removeRiskScoringTask, startRiskScoringTask } from './tasks'; import { createIndex } from './utils/create_index'; +import { bulkDeleteSavedObjects } from '../risk_score/prebuilt_saved_objects/helpers/bulk_delete_saved_objects'; interface InitOpts { namespace: string; @@ -212,6 +214,17 @@ export class RiskEngineDataClient { namespace, }); + const deleteDashboardsPromises = [RiskScoreEntity.host, RiskScoreEntity.user].map((entity) => + bulkDeleteSavedObjects({ + deleteAll: true, + savedObjectsClient: this.options.soClient, + spaceId: namespace, + savedObjectTemplate: `${entity}RiskScoreDashboards`, + }) + ); + + await Promise.all(deleteDashboardsPromises); + const newlegacyRiskEngineStatus = await this.getLegacyStatus({ namespace }); return newlegacyRiskEngineStatus === RiskEngineStatus.NOT_INSTALLED; diff --git a/x-pack/plugins/security_solution/tsconfig.json b/x-pack/plugins/security_solution/tsconfig.json index 4ef9c7499474c..9604dae1909ce 100644 --- a/x-pack/plugins/security_solution/tsconfig.json +++ b/x-pack/plugins/security_solution/tsconfig.json @@ -165,10 +165,12 @@ "@kbn/security-solution-upselling", "@kbn/discover-plugin", "@kbn/data-view-editor-plugin", - "@kbn/navigation-plugin", "@kbn/alerts-ui-shared", + "@kbn/saved-search-plugin", + "@kbn/unified-histogram-plugin", + "@kbn/navigation-plugin", "@kbn/core-logging-server-mocks", "@kbn/core-lifecycle-browser", - "@kbn/handlebars", + "@kbn/handlebars" ] } diff --git a/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/categories.ts b/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/categories.ts index 6f2995b27939c..ebefe9b77a70b 100644 --- a/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/categories.ts +++ b/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/categories.ts @@ -20,6 +20,7 @@ export const CATEGORIES: SeparatorLinkCategory[] = [ { type: LinkCategoryType.separator, linkIds: [ + SecurityPageName.rulesLanding, SecurityPageName.alerts, SecurityPageName.cloudSecurityPostureFindings, SecurityPageName.case, @@ -35,7 +36,7 @@ export const CATEGORIES: SeparatorLinkCategory[] = [ }, { type: LinkCategoryType.separator, - linkIds: [ExternalPageName.fleet, SecurityPageName.assets, SecurityPageName.rulesLanding], + linkIds: [ExternalPageName.fleet, SecurityPageName.assets], }, { type: LinkCategoryType.separator, diff --git a/x-pack/plugins/security_solution_serverless/server/common/services/usage_reporting_service.ts b/x-pack/plugins/security_solution_serverless/server/common/services/usage_reporting_service.ts index aae47c8de9f24..4f5daeae034b9 100644 --- a/x-pack/plugins/security_solution_serverless/server/common/services/usage_reporting_service.ts +++ b/x-pack/plugins/security_solution_serverless/server/common/services/usage_reporting_service.ts @@ -7,16 +7,20 @@ import type { Response } from 'node-fetch'; import fetch from 'node-fetch'; +import https from 'https'; import { USAGE_SERVICE_USAGE_URL } from '../../constants'; import type { UsageRecord } from '../../types'; +// TODO remove once we have the CA available +const agent = new https.Agent({ rejectUnauthorized: false }); export class UsageReportingService { public async reportUsage(records: UsageRecord[]): Promise { return fetch(USAGE_SERVICE_USAGE_URL, { method: 'post', body: JSON.stringify(records), headers: { 'Content-Type': 'application/json' }, + agent, }); } } diff --git a/x-pack/plugins/serverless_search/public/application/components/overview.tsx b/x-pack/plugins/serverless_search/public/application/components/overview.tsx index 3d7783ee9a50a..e8ef03a72a1f0 100644 --- a/x-pack/plugins/serverless_search/public/application/components/overview.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/overview.tsx @@ -203,7 +203,7 @@ export const ElasticsearchOverview = () => { 'ingestData', codeSnippetArguments )} - showTryInConsole={getConsoleRequest('ingestData')} + consoleRequest={getConsoleRequest('ingestData')} languages={languageDefinitions} selectedLanguage={selectedLanguage} setSelectedLanguage={setSelectedLanguage} diff --git a/x-pack/plugins/stack_connectors/common/gen_ai/schema.ts b/x-pack/plugins/stack_connectors/common/gen_ai/schema.ts index 0ec4541120444..bae0a9f532d41 100644 --- a/x-pack/plugins/stack_connectors/common/gen_ai/schema.ts +++ b/x-pack/plugins/stack_connectors/common/gen_ai/schema.ts @@ -41,20 +41,29 @@ export const GenAiRunActionResponseSchema = schema.object( object: schema.string(), created: schema.number(), model: schema.string(), - usage: schema.object({ - prompt_tokens: schema.number(), - completion_tokens: schema.number(), - total_tokens: schema.number(), - }), + usage: schema.object( + { + prompt_tokens: schema.number(), + completion_tokens: schema.number(), + total_tokens: schema.number(), + }, + { unknowns: 'ignore' } + ), choices: schema.arrayOf( - schema.object({ - message: schema.object({ - role: schema.string(), - content: schema.string(), - }), - finish_reason: schema.string(), - index: schema.number(), - }) + schema.object( + { + message: schema.object( + { + role: schema.string(), + content: schema.string(), + }, + { unknowns: 'ignore' } + ), + finish_reason: schema.string(), + index: schema.number(), + }, + { unknowns: 'ignore' } + ) ), }, { unknowns: 'ignore' } diff --git a/x-pack/plugins/stack_connectors/server/connector_types/gen_ai/gen_ai.ts b/x-pack/plugins/stack_connectors/server/connector_types/gen_ai/gen_ai.ts index 488ec89711415..c88d129db7071 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/gen_ai/gen_ai.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/gen_ai/gen_ai.ts @@ -77,7 +77,7 @@ export class GenAiConnector extends SubActionConnector): string { if (!error.response?.status) { - return 'Unknown API Error'; + return `Unexpected API Error: ${error.code} - ${error.message}`; } if (error.response.status === 401) { return 'Unauthorized API Error'; @@ -116,6 +116,7 @@ export class GenAiConnector extends SubActionConnector { return useMemo(() => { if (monitor?.created_at) { - const diff = moment(monitor?.created_at).diff(moment().subtract(30, 'day'), 'days'); - if (diff > 0) { - return { to, from: monitor?.created_at, loading }; + const monitorCreatedDaysAgo = moment().diff(monitor.created_at, 'days'); + const isProjectMonitor = monitor?.[ConfigKey.MONITOR_SOURCE_TYPE] === SourceType.PROJECT; + + // Always look back at lest 3 days to account for reinstated project monitors. + if ((!isProjectMonitor || monitorCreatedDaysAgo > 3) && monitorCreatedDaysAgo < 30) { + return { to, from: monitor.created_at, loading }; } } return { to, from, loading }; - }, [monitor?.created_at, to, from, loading]); + }, [monitor, to, from, loading]); }; diff --git a/x-pack/plugins/task_manager/server/saved_objects/migrations.ts b/x-pack/plugins/task_manager/server/saved_objects/migrations.ts index bcbd493f83160..83bd683fdba0f 100644 --- a/x-pack/plugins/task_manager/server/saved_objects/migrations.ts +++ b/x-pack/plugins/task_manager/server/saved_objects/migrations.ts @@ -16,6 +16,7 @@ import { } from '@kbn/core/server'; import type { RuleTaskState, + MutableRuleTaskState, TrackedLifecycleAlertState, WrappedLifecycleRuleState, } from '@kbn/alerting-state-types'; @@ -253,7 +254,7 @@ function addAlertUUID(doc: SavedObjectUnsanitizedDoc, currentUUIDs: Map ): void { diff --git a/x-pack/plugins/task_manager/server/saved_objects/migrations_880.test.ts b/x-pack/plugins/task_manager/server/saved_objects/migrations_880.test.ts index c8c0d0bf91ed3..1fee82adc4cf5 100644 --- a/x-pack/plugins/task_manager/server/saved_objects/migrations_880.test.ts +++ b/x-pack/plugins/task_manager/server/saved_objects/migrations_880.test.ts @@ -11,6 +11,7 @@ import { migrationMocks } from '@kbn/core/server/mocks'; import { SavedObjectsUtils } from '@kbn/core-saved-objects-utils-server'; import type { RuleTaskState, + MutableRuleTaskState, WrappedLifecycleRuleState, RawAlertInstance, } from '@kbn/alerting-state-types'; @@ -73,7 +74,7 @@ describe('successful migrations for 8.8.0', () => { }); function checkMetaInRuleTaskState( - actual: RuleTaskState, + actual: MutableRuleTaskState, original: RuleTaskState, wrappedUUIDs?: Map ) { diff --git a/x-pack/plugins/threat_intelligence/public/utils/search.ts b/x-pack/plugins/threat_intelligence/public/utils/search.ts index e54bd6ecef885..1dac39f74b230 100644 --- a/x-pack/plugins/threat_intelligence/public/utils/search.ts +++ b/x-pack/plugins/threat_intelligence/public/utils/search.ts @@ -9,7 +9,6 @@ import { IEsSearchRequest, IKibanaSearchResponse, isCompleteResponse, - isErrorResponse, } from '@kbn/data-plugin/common'; import { ISearchStart } from '@kbn/data-plugin/public'; import { RequestAdapter } from '@kbn/inspector-plugin/common'; @@ -97,9 +96,6 @@ export const search = async ( if (isCompleteResponse(response)) { inspect.recordRequestCompletion(searchRequest, response); resolve(response.rawResponse); - } else if (isErrorResponse(response)) { - inspect.recordRequestError(response); - reject(response); } }, error: (requestError) => { diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index bfff9d80f7f39..e1f61483a6b20 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -31003,7 +31003,6 @@ "xpack.securitySolution.detectionEngine.createRule.savedQueryFiltersLabel": "Filtres de requête enregistrés", "xpack.securitySolution.detectionEngine.createRule.savedQueryLabel": "Requête enregistrée", "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.authorFieldEmptyError": "L'auteur doit être renseigné", - "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.dataViewSelector": "Vue de données", "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.descriptionFieldRequiredError": "Une description est requise.", "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.fiedIndexPatternsLabel": "Modèles d'indexation", "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.fieldAssociatedToEndpointListLabel": "Ajouter des exceptions de point de terminaison existantes à la règle", @@ -33570,7 +33569,6 @@ "xpack.securitySolution.kubernetes.columnPod": "Pod", "xpack.securitySolution.kubernetes.columnSessionStart": "Date de démarrage", "xpack.securitySolution.landing.threatHunting.hostsDescription": "Aperçu complet de tous les hôtes et événements de sécurité des hôtes.", - "xpack.securitySolution.lastEventTime.errorSearchDescription": "Une erreur s'est produite sur la recherche de dernière heure de l'événement", "xpack.securitySolution.lastEventTime.failSearchDescription": "Impossible de lancer une recherche sur la dernière heure de l'événement", "xpack.securitySolution.lensEmbeddable.NoDataToDisplay.title": "Aucune donnée à afficher", "xpack.securitySolution.licensing.unsupportedMachineLearningMessage": "Votre licence ne prend pas en charge le Machine Learning. Veuillez mettre votre licence à niveau.", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 443e6725a422a..0afa87b22236a 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -31002,7 +31002,6 @@ "xpack.securitySolution.detectionEngine.createRule.savedQueryFiltersLabel": "保存されたクエリフィルター", "xpack.securitySolution.detectionEngine.createRule.savedQueryLabel": "保存されたクエリ", "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.authorFieldEmptyError": "作成者は空にする必要があります", - "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.dataViewSelector": "データビュー", "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.descriptionFieldRequiredError": "説明が必要です。", "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.fiedIndexPatternsLabel": "インデックスパターン", "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.fieldAssociatedToEndpointListLabel": "既存のエンドポイント例外をルールに追加", @@ -33569,7 +33568,6 @@ "xpack.securitySolution.kubernetes.columnPod": "ポッド", "xpack.securitySolution.kubernetes.columnSessionStart": "開始日", "xpack.securitySolution.landing.threatHunting.hostsDescription": "すべてのホストとホスト関連のセキュリティイベントに関する包括的な概要。", - "xpack.securitySolution.lastEventTime.errorSearchDescription": "前回のイベント時刻検索でエラーが発生しました。", "xpack.securitySolution.lastEventTime.failSearchDescription": "前回のイベント時刻で検索を実行できませんでした", "xpack.securitySolution.lensEmbeddable.NoDataToDisplay.title": "表示するデータがありません", "xpack.securitySolution.licensing.unsupportedMachineLearningMessage": "ご使用のライセンスは機械翻訳をサポートしていません。ライセンスをアップグレードしてください。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index f4f6bf13a8f28..57172509567f9 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -30998,7 +30998,6 @@ "xpack.securitySolution.detectionEngine.createRule.savedQueryFiltersLabel": "已保存查询筛选", "xpack.securitySolution.detectionEngine.createRule.savedQueryLabel": "已保存查询", "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.authorFieldEmptyError": "作者不得为空", - "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.dataViewSelector": "数据视图", "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.descriptionFieldRequiredError": "描述必填。", "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.fiedIndexPatternsLabel": "索引模式", "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.fieldAssociatedToEndpointListLabel": "将现有的终端例外添加到规则", @@ -33565,7 +33564,6 @@ "xpack.securitySolution.kubernetes.columnPod": "Pod", "xpack.securitySolution.kubernetes.columnSessionStart": "开始日期", "xpack.securitySolution.landing.threatHunting.hostsDescription": "所有主机和主机相关安全事件的全面概览。", - "xpack.securitySolution.lastEventTime.errorSearchDescription": "搜索上次事件时间时发生错误", "xpack.securitySolution.lastEventTime.failSearchDescription": "无法对上次事件时间执行搜索", "xpack.securitySolution.lensEmbeddable.NoDataToDisplay.title": "没有可显示的数据", "xpack.securitySolution.licensing.unsupportedMachineLearningMessage": "您的许可证不支持 Machine Learning。请升级您的许可证。", diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/state.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/state.test.ts index 40047259de4d9..364697507218b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/state.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/state.test.ts @@ -82,7 +82,7 @@ describe('loadRuleState', () => { meta: { lastScheduledActions: { group: 'first_group', - date: new Date('2020-02-09T23:15:41.941Z'), + date: '2020-02-09T23:15:41.941Z', }, }, }, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/state.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/state.ts index 3f9fc3d61097e..cd818689571e1 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/state.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/state.ts @@ -5,10 +5,6 @@ * 2.0. */ import { HttpSetup } from '@kbn/core/public'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { fold } from 'fp-ts/lib/Either'; -import { Errors, identity } from 'io-ts'; -import { ruleStateSchema } from '@kbn/alerting-plugin/common'; import { AsApiContract, RewriteRequestCase } from '@kbn/actions-plugin/common'; import { RuleTaskState } from '../../../types'; import { INTERNAL_BASE_ALERTING_API_PATH } from '../../constants'; @@ -33,17 +29,9 @@ export async function loadRuleState({ http: HttpSetup; ruleId: string; }): Promise { - return await http + return (await http .get | EmptyHttpResponse>( `${INTERNAL_BASE_ALERTING_API_PATH}/rule/${ruleId}/state` ) - .then((state) => (state ? rewriteBodyRes(state) : {})) - .then((state: RuleTaskState) => { - return pipe( - ruleStateSchema.decode(state), - fold((e: Errors) => { - throw new Error(`Rule "${ruleId}" has invalid state`); - }, identity) - ); - }); + .then((state) => (state ? rewriteBodyRes(state) : {}))) as RuleTaskState; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update_api_key.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update_api_key.test.ts index 15d1e6a58596e..2b643cfc07cc7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update_api_key.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update_api_key.test.ts @@ -18,7 +18,7 @@ describe('updateAPIKey', () => { expect(http.post.mock.calls).toMatchInlineSnapshot(` Array [ Array [ - "/internal/alerting/rule/1%2F/_update_api_key", + "/api/alerting/rule/1%2F/_update_api_key", ], ] `); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update_api_key.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update_api_key.ts index f9a6912c1d43f..1b83238559d1e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update_api_key.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update_api_key.ts @@ -6,12 +6,12 @@ */ import { HttpSetup } from '@kbn/core/public'; import { KueryNode } from '@kbn/es-query'; -import { INTERNAL_BASE_ALERTING_API_PATH } from '../../constants'; +import { BASE_ALERTING_API_PATH, INTERNAL_BASE_ALERTING_API_PATH } from '../../constants'; import { BulkEditResponse } from '../../../types'; export async function updateAPIKey({ id, http }: { id: string; http: HttpSetup }): Promise { return http.post( - `${INTERNAL_BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(id)}/_update_api_key` + `${BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(id)}/_update_api_key` ); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_fetch_alerts.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_fetch_alerts.test.tsx index 06c712ef87e2b..ef515cf8ffdca 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_fetch_alerts.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_fetch_alerts.test.tsx @@ -6,7 +6,7 @@ */ import sinon from 'sinon'; -import { of } from 'rxjs'; +import { of, throwError } from 'rxjs'; import { act, renderHook } from '@testing-library/react-hooks'; import { useFetchAlerts, FetchAlertsArgs, FetchAlertResp } from './use_fetch_alerts'; import { useKibana } from '../../../../common/lib/kibana'; @@ -286,9 +286,10 @@ describe('useFetchAlerts', () => { ]); }); - it('returns the correct response if the request is undefined', () => { - // @ts-expect-error - const obs$ = of(undefined); + it('handles search error', () => { + const obs$ = throwError( + 'simulated search response error, which could be 1) undefined response, 2) response without rawResponse, or 3) partial response' + ); dataSearchMock.mockReturnValue(obs$); const { result } = renderHook(() => useFetchAlerts(args)); @@ -308,7 +309,7 @@ describe('useFetchAlerts', () => { expect(showErrorMock).toHaveBeenCalled(); }); - it('returns the correct response if the request is running', () => { + it('returns the correct response if the search response is running', () => { const obs$ = of({ ...searchResponse, isRunning: true }); dataSearchMock.mockReturnValue(obs$); const { result } = renderHook(() => useFetchAlerts(args)); @@ -327,47 +328,6 @@ describe('useFetchAlerts', () => { ]); }); - it('returns the correct response if the request is partial', () => { - const obs$ = of({ ...searchResponse, isPartial: true }); - dataSearchMock.mockReturnValue(obs$); - const { result } = renderHook(() => useFetchAlerts(args)); - - expect(result.current).toEqual([ - false, - { - ...expectedResponse, - alerts: [], - getInspectQuery: expect.anything(), - refetch: expect.anything(), - isInitializing: true, - totalAlerts: -1, - updatedAt: 0, - }, - ]); - expect(showErrorMock).toHaveBeenCalled(); - }); - - it('returns the correct response if there is no rawResponse', () => { - // @ts-expect-error - const obs$ = of({ id: '1', isRunning: true, isPartial: false }); - dataSearchMock.mockReturnValue(obs$); - const { result } = renderHook(() => useFetchAlerts(args)); - - expect(result.current).toEqual([ - false, - { - ...expectedResponse, - alerts: [], - getInspectQuery: expect.anything(), - refetch: expect.anything(), - isInitializing: true, - totalAlerts: -1, - updatedAt: 0, - }, - ]); - expect(showErrorMock).toHaveBeenCalled(); - }); - it('returns the correct total alerts if the total alerts in the response is an object', () => { const obs$ = of({ ...searchResponse, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_fetch_alerts.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_fetch_alerts.tsx index d509c2dcf98f7..374e2883cc089 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_fetch_alerts.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_fetch_alerts.tsx @@ -12,7 +12,7 @@ import { noop } from 'lodash'; import { useCallback, useEffect, useReducer, useRef, useMemo } from 'react'; import { Subscription } from 'rxjs'; -import { isCompleteResponse, isErrorResponse } from '@kbn/data-plugin/common'; +import { isCompleteResponse } from '@kbn/data-plugin/common'; import type { RuleRegistrySearchRequest, RuleRegistrySearchRequestPagination, @@ -27,7 +27,6 @@ import type { import type { Alert, Alerts, GetInspectQuery, InspectQuery } from '../../../../types'; import { useKibana } from '../../../../common/lib/kibana'; import { DefaultSort } from './constants'; -import * as i18n from './translations'; export interface FetchAlertsArgs { featureIds: ValidFeatureId[]; @@ -260,10 +259,6 @@ const useFetchAlerts = ({ totalAlerts, }); searchSubscription$.current.unsubscribe(); - } else if (isErrorResponse(response)) { - dispatch({ type: 'loading', loading: false }); - data.search.showError(new Error(i18n.ERROR_FETCH_ALERTS)); - searchSubscription$.current.unsubscribe(); } }, error: (msg) => { diff --git a/x-pack/test/alerting_api_integration/common/lib/alert_utils.ts b/x-pack/test/alerting_api_integration/common/lib/alert_utils.ts index 74354c15af0ce..480c4baaab198 100644 --- a/x-pack/test/alerting_api_integration/common/lib/alert_utils.ts +++ b/x-pack/test/alerting_api_integration/common/lib/alert_utils.ts @@ -43,6 +43,15 @@ interface UpdateAlwaysFiringAction { overwrites: Record; } +const SNOOZE_SCHEDULE = { + rRule: { + dtstart: '2021-03-07T00:00:00.000Z', + tzid: 'UTC', + count: 1, + }, + duration: 864000000, +}; + export class AlertUtils { private referenceCounter = 1; private readonly user?: User; @@ -105,7 +114,11 @@ export class AlertUtils { const request = this.supertestWithoutAuth .post(`${getUrlPrefix(this.space.id)}/internal/alerting/rule/${alertId}/_snooze`) .set('kbn-xsrf', 'foo') - .set('content-type', 'application/json'); + .set('content-type', 'application/json') + .send({ + snooze_schedule: SNOOZE_SCHEDULE, + }); + if (this.user) { return request.auth(this.user.username, this.user.password); } @@ -116,7 +129,10 @@ export class AlertUtils { const request = this.supertestWithoutAuth .post(`${getUrlPrefix(this.space.id)}/internal/alerting/rule/${alertId}/_unsnooze`) .set('kbn-xsrf', 'foo') - .set('content-type', 'application/json'); + .set('content-type', 'application/json') + .send({ + schedule_ids: [alertId], + }); if (this.user) { return request.auth(this.user.username, this.user.password); } @@ -167,7 +183,7 @@ export class AlertUtils { public getUpdateApiKeyRequest(alertId: string) { const request = this.supertestWithoutAuth - .post(`${getUrlPrefix(this.space.id)}/internal/alerting/rule/${alertId}/_update_api_key`) + .post(`${getUrlPrefix(this.space.id)}/api/alerting/rule/${alertId}/_update_api_key`) .set('kbn-xsrf', 'foo'); if (this.user) { return request.auth(this.user.username, this.user.password); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/index.ts index ea009fc24bbc6..1a2a69cd95d69 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/index.ts @@ -44,6 +44,7 @@ export default function alertingTests({ loadTestFile, getService }: FtrProviderC loadTestFile(require.resolve('./health')); loadTestFile(require.resolve('./excluded')); loadTestFile(require.resolve('./snooze')); + loadTestFile(require.resolve('./unsnooze')); loadTestFile(require.resolve('./global_execution_log')); loadTestFile(require.resolve('./get_global_execution_kpi')); loadTestFile(require.resolve('./get_action_error_log')); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/unsnooze.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/unsnooze.ts index 8b6a8aa2c6c45..39b5b720ccda3 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/unsnooze.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/unsnooze.ts @@ -27,7 +27,6 @@ export default function createUnsnoozeRuleTests({ getService }: FtrProviderConte const objectRemover = new ObjectRemover(supertest); after(() => objectRemover.removeAll()); - for (const scenario of UserAtSpaceScenarios) { const { user, space } = scenario; const alertUtils = new AlertUtils({ user, space, supertestWithoutAuth }); @@ -98,7 +97,7 @@ export default function createUnsnoozeRuleTests({ getService }: FtrProviderConte .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); - expect(updatedAlert.snooze_schedule).to.eql(null); + expect(updatedAlert.snooze_schedule).to.eql([]); expect(updatedAlert.mute_all).to.eql(false); // Ensure AAD isn't broken await checkAAD({ @@ -155,7 +154,7 @@ export default function createUnsnoozeRuleTests({ getService }: FtrProviderConte .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); - expect(updatedAlert.snooze_schedule).to.eql(null); + expect(updatedAlert.snooze_schedule).to.eql([]); expect(updatedAlert.mute_all).to.eql(false); // Ensure AAD isn't broken await checkAAD({ @@ -223,7 +222,7 @@ export default function createUnsnoozeRuleTests({ getService }: FtrProviderConte .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); - expect(updatedAlert.snooze_schedule).to.eql(null); + expect(updatedAlert.snooze_schedule).to.eql([]); expect(updatedAlert.mute_all).to.eql(false); // Ensure AAD isn't broken await checkAAD({ @@ -291,7 +290,7 @@ export default function createUnsnoozeRuleTests({ getService }: FtrProviderConte .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); - expect(updatedAlert.snooze_schedule).to.eql(null); + expect(updatedAlert.snooze_schedule).to.eql([]); expect(updatedAlert.mute_all).to.eql(false); // Ensure AAD isn't broken await checkAAD({ diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/user_managed_api_key.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/user_managed_api_key.ts index 7a92b9e11d859..905fe05e110e1 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/user_managed_api_key.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/user_managed_api_key.ts @@ -397,7 +397,7 @@ export default function userManagedApiKeyTest({ getService }: FtrProviderContext .post( `${getUrlPrefix( SuperuserAtSpace1.space.id - )}/internal/alerting/rule/${ruleId}/_update_api_key` + )}/api/alerting/rule/${ruleId}/_update_api_key` ) .set('kbn-xsrf', 'foo') .set('Authorization', `ApiKey ${apiKey}`); @@ -422,7 +422,7 @@ export default function userManagedApiKeyTest({ getService }: FtrProviderContext .post( `${getUrlPrefix( SuperuserAtSpace1.space.id - )}/internal/alerting/rule/${ruleId}/_update_api_key` + )}/api/alerting/rule/${ruleId}/_update_api_key` ) .set('kbn-xsrf', 'foo'); expect(response.status).to.eql(204); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/index.ts index 0adcfe10009ca..fda6c1b9c80b2 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/index.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/index.ts @@ -21,6 +21,7 @@ export default function alertingTests({ loadTestFile, getService }: FtrProviderC loadTestFile(require.resolve('./ephemeral')); loadTestFile(require.resolve('./event_log_alerts')); loadTestFile(require.resolve('./snooze')); + loadTestFile(require.resolve('./unsnooze')); loadTestFile(require.resolve('./bulk_edit')); loadTestFile(require.resolve('./capped_action_type')); loadTestFile(require.resolve('./scheduled_task_id')); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/snooze.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/snooze.ts index 110acf31039ae..a658e461917f4 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/snooze.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/snooze.ts @@ -35,8 +35,7 @@ export default function createSnoozeRuleTests({ getService }: FtrProviderContext const log = getService('log'); const retry = getService('retry'); - // FLAKY: https://github.com/elastic/kibana/issues/159076 - describe.skip('snooze', () => { + describe('snooze', () => { const objectRemover = new ObjectRemover(supertest); after(() => objectRemover.removeAll()); @@ -354,11 +353,19 @@ export default function createSnoozeRuleTests({ getService }: FtrProviderContext .expect(200); objectRemover.add(Spaces.space1.id, createdRule.id, 'rule', 'alerting'); - const response = await alertUtils.getSnoozeRequest(createdRule.id).send({ - snooze_schedule: { - ...SNOOZE_SCHEDULE, - duration: 3000, + const dateStart = new Date().toISOString(); + const snooze = { + ...SNOOZE_SCHEDULE, + rRule: { + ...SNOOZE_SCHEDULE.rRule, + // updating the dtstart to the current time because otherwise the snooze might be over already + dtstart: dateStart, }, + duration: 3000, + }; + + const response = await alertUtils.getSnoozeRequest(createdRule.id).send({ + snooze_schedule: snooze, }); expect(response.statusCode).to.eql(204); @@ -369,12 +376,7 @@ export default function createSnoozeRuleTests({ getService }: FtrProviderContext .get(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/${createdRule.id}`) .set('kbn-xsrf', 'foo') .expect(200); - expect(updatedAlert.snooze_schedule).to.eql([ - { - ...SNOOZE_SCHEDULE, - duration: 3000, - }, - ]); + expect(updatedAlert.snooze_schedule).to.eql([snooze]); }); log.info('wait for snoozing to end'); await retry.try(async () => { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/unsnooze.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/unsnooze.ts index 4c3937d23190c..81e246de3214a 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/unsnooze.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/unsnooze.ts @@ -30,7 +30,7 @@ export default function createSnoozeRuleTests({ getService }: FtrProviderContext it('should handle unsnooze rule request appropriately', async () => { const { body: createdAction } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}}/api/actions/connector`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'MY action', @@ -58,7 +58,7 @@ export default function createSnoozeRuleTests({ getService }: FtrProviderContext .expect(200); objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); - const response = await alertUtils.getSnoozeRequest(createdAlert.id); + const response = await alertUtils.getUnsnoozeRequest(createdAlert.id); expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); diff --git a/x-pack/test/apm_api_integration/tests/settings/apm_indices/apm_indices.spec.ts b/x-pack/test/apm_api_integration/tests/settings/apm_indices/apm_indices.spec.ts index 5aef295ddb324..6798d22af1999 100644 --- a/x-pack/test/apm_api_integration/tests/settings/apm_indices/apm_indices.spec.ts +++ b/x-pack/test/apm_api_integration/tests/settings/apm_indices/apm_indices.spec.ts @@ -49,6 +49,7 @@ export default function apmIndicesTests({ getService }: FtrProviderContext) { error: 'logs-apm*,apm-*', metric: 'metrics-apm*,apm-*', onboarding: 'apm-*', + sourcemap: 'apm-*', }); }); diff --git a/x-pack/test/detection_engine_api_integration/basic/tests/create_rules.ts b/x-pack/test/detection_engine_api_integration/basic/tests/create_rules.ts index 00307619d8477..fbbef20984642 100644 --- a/x-pack/test/detection_engine_api_integration/basic/tests/create_rules.ts +++ b/x-pack/test/detection_engine_api_integration/basic/tests/create_rules.ts @@ -77,7 +77,6 @@ export default ({ getService }: FtrProviderContext) => { author: [], created_by: 'elastic', description: 'Simple Rule Query', - investigation_fields: [], enabled: true, false_positives: [], from: 'now-6m', diff --git a/x-pack/test/detection_engine_api_integration/common/config.ts b/x-pack/test/detection_engine_api_integration/common/config.ts index 7f3598ee8d5e8..c4c3c44f1c418 100644 --- a/x-pack/test/detection_engine_api_integration/common/config.ts +++ b/x-pack/test/detection_engine_api_integration/common/config.ts @@ -78,7 +78,6 @@ export function createTestConfig(options: CreateTestConfigOptions, testFiles?: s 'previewTelemetryUrlEnabled', 'riskScoringPersistence', 'riskScoringRoutesEnabled', - 'detectionsCoverageOverview', ])}`, '--xpack.task_manager.poll_interval=1000', `--xpack.actions.preconfigured=${JSON.stringify({ diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules.ts index aa32e1ebb114b..d8fc2ec1439be 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules.ts @@ -213,7 +213,6 @@ export default ({ getService }: FtrProviderContext) => { risk_score_mapping: [], name: 'Simple Rule Query', query: 'user.name: root or user.name: admin', - investigation_fields: [], references: [], related_integrations: [], required_fields: [], diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/export_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/export_rules.ts index 7a4afd6d44d85..44ac35cf9569d 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/export_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/export_rules.ts @@ -754,6 +754,5 @@ function expectToMatchRuleSchema(obj: RuleResponse): void { index: expect.arrayContaining([]), query: expect.any(String), actions: expect.arrayContaining([]), - investigation_fields: expect.arrayContaining([]), }); } diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/import_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/import_rules.ts index b14e0e87a37c0..b07a54f86d2f6 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/import_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/import_rules.ts @@ -25,6 +25,7 @@ import { deleteAllAlerts, getSimpleRule, getSimpleRuleAsNdjson, + getRulesAsNdjson, getSimpleRuleOutput, getThresholdRuleForSignalTesting, getWebHookAction, @@ -1868,6 +1869,21 @@ export default ({ getService }: FtrProviderContext): void => { }); }); }); + + it('should import a rule with "investigation_fields', async () => { + await supertest + .post(`${DETECTION_ENGINE_RULES_URL}/_import`) + .set('kbn-xsrf', 'true') + .attach( + 'file', + getRulesAsNdjson([ + { ...getSimpleRule(), investigation_fields: { field_names: ['foo'] } }, + ]), + 'rules.ndjson' + ) + .expect('Content-Type', 'application/json; charset=utf-8') + .expect(200); + }); }); }); }; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules.ts index 498dde28a632e..09e2c7d84c0a8 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules.ts @@ -445,15 +445,15 @@ export default ({ getService }: FtrProviderContext) => { }); describe('investigation_fields', () => { - it('should overwrite investigation_fields value on update - non additive', async () => { + it('should overwrite investigation_fields value on patch - non additive', async () => { await createRule(supertest, log, { ...getSimpleRule('rule-1'), - investigation_fields: ['blob', 'boop'], + investigation_fields: { field_names: ['blob', 'boop'] }, }); const rulePatch = { rule_id: 'rule-1', - investigation_fields: ['foo', 'bar'], + investigation_fields: { field_names: ['foo', 'bar'] }, }; const { body } = await supertest @@ -463,7 +463,47 @@ export default ({ getService }: FtrProviderContext) => { .send(rulePatch) .expect(200); - expect(body.investigation_fields).to.eql(['foo', 'bar']); + expect(body.investigation_fields.field_names).to.eql(['foo', 'bar']); + }); + + it('should not allow field to be unset', async () => { + await createRule(supertest, log, { + ...getSimpleRule('rule-1'), + investigation_fields: { field_names: ['blob', 'boop'] }, + }); + + const rulePatch = { + rule_id: 'rule-1', + investigation_fields: undefined, + }; + + const { body } = await supertest + .patch(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send(rulePatch) + .expect(200); + + expect(body.investigation_fields).to.eql({ field_names: ['blob', 'boop'] }); + }); + + it('should not unset investigation_fields if not specified in patch', async () => { + await createRule(supertest, log, { + ...getSimpleRule('rule-1'), + investigation_fields: { field_names: ['blob', 'boop'] }, + }); + + const rulePatch = { + rule_id: 'rule-1', + name: 'New name', + }; + + const { body } = await supertest + .patch(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send(rulePatch) + .expect(200); + + expect(body.investigation_fields.field_names).to.eql(['blob', 'boop']); }); }); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/risk_engine/init_and_status_apis.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/risk_engine/init_and_status_apis.ts index 0f607843f4107..0babe434c7f90 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/risk_engine/init_and_status_apis.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/risk_engine/init_and_status_apis.ts @@ -15,6 +15,9 @@ import { clearLegacyTransforms, riskEngineRouteHelpersFactory, clearTransforms, + installLegacyRiskScore, + getLegacyRiskScoreDashboards, + clearLegacyDashboards, } from './utils'; // eslint-disable-next-line import/no-default-export @@ -38,6 +41,10 @@ export default ({ getService }: FtrProviderContext) => { es, log, }); + await clearLegacyDashboards({ + supertest, + log, + }); }); describe('init api', () => { @@ -328,7 +335,7 @@ export default ({ getService }: FtrProviderContext) => { }); it('should remove legacy risk score transform if it exists', async () => { - await createLegacyTransforms({ es }); + await installLegacyRiskScore({ supertest }); for (const transformId of legacyTransformIds) { const tr = await es.transform.getTransform({ @@ -338,6 +345,12 @@ export default ({ getService }: FtrProviderContext) => { expect(tr?.transforms?.[0]?.id).to.eql(transformId); } + const legacyDashboards = await getLegacyRiskScoreDashboards({ + kibanaServer, + }); + + expect(legacyDashboards.length).to.eql(4); + await riskEngineRoutes.init(); for (const transformId of legacyTransformIds) { @@ -349,6 +362,12 @@ export default ({ getService }: FtrProviderContext) => { expect(err).to.not.be(undefined); } } + + const legacyDashboardsAfterInit = await getLegacyRiskScoreDashboards({ + kibanaServer, + }); + + expect(legacyDashboardsAfterInit.length).to.eql(0); }); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/risk_engine/utils.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/risk_engine/utils.ts index b0fbfc4e92c32..0d0cbd3d36d06 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/risk_engine/utils.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/risk_engine/utils.ts @@ -6,7 +6,7 @@ */ import { v4 as uuidv4 } from 'uuid'; -import type SuperTest from 'supertest'; +import SuperTest from 'supertest'; import type { Client } from '@elastic/elasticsearch'; import type { ToolingLog } from '@kbn/tooling-log'; import type { EcsRiskScore, RiskScore } from '@kbn/security-solution-plugin/common/risk_engine'; @@ -346,6 +346,34 @@ export const clearLegacyTransforms = async ({ } }; +export const clearLegacyDashboards = async ({ + supertest, + log, +}: { + supertest: SuperTest.SuperTest; + log: ToolingLog; +}): Promise => { + try { + await supertest + .post( + '/internal/risk_score/prebuilt_content/saved_objects/_bulk_delete/hostRiskScoreDashboards' + ) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + await supertest + .post( + '/internal/risk_score/prebuilt_content/saved_objects/_bulk_delete/userRiskScoreDashboards' + ) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + } catch (e) { + log.error(`Error deleting legacy dashboards: ${e.message}`); + } +}; + export const createLegacyTransforms = async ({ es }: { es: Client }): Promise => { const transforms = legacyTransformIds.map((transform) => es.transform.putTransform({ @@ -379,6 +407,18 @@ export const createLegacyTransforms = async ({ es }: { es: Client }): Promise { + const savedObejectLens = await kibanaServer.savedObjects.find({ + type: 'lens', + }); + + return savedObejectLens?.saved_objects.filter((s) => s?.attributes?.title?.includes('Risk')); +}; + export const riskEngineRouteHelpersFactory = ( supertest: SuperTest.SuperTest, namespace?: string @@ -411,3 +451,37 @@ export const riskEngineRouteHelpersFactory = ( .send() .expect(200), }); + +export const installLegacyRiskScore = async ({ + supertest, +}: { + supertest: SuperTest.SuperTest; +}) => { + await supertest + .post('/internal/risk_score') + .set('kbn-xsrf', 'true') + .send({ riskScoreEntity: 'host' }) + .expect(200); + + await supertest + .post('/internal/risk_score') + .set('kbn-xsrf', 'true') + .send({ riskScoreEntity: 'user' }) + .expect(200); + + await supertest + .post( + '/internal/risk_score/prebuilt_content/saved_objects/_bulk_create/hostRiskScoreDashboards' + ) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + await supertest + .post( + '/internal/risk_score/prebuilt_content/saved_objects/_bulk_create/userRiskScoreDashboards' + ) + .set('kbn-xsrf', 'true') + .send() + .expect(200); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts index dad8b37321e7b..226432cbdbb39 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts @@ -870,12 +870,12 @@ export default ({ getService }: FtrProviderContext) => { it('should overwrite investigation_fields value on update - non additive', async () => { await createRule(supertest, log, { ...getSimpleRule('rule-1'), - investigation_fields: ['blob', 'boop'], + investigation_fields: { field_names: ['blob', 'boop'] }, }); const ruleUpdate = { ...getSimpleRuleUpdate('rule-1'), - investigation_fields: ['foo', 'bar'], + investigation_fields: { field_names: ['foo', 'bar'] }, }; const { body } = await supertest @@ -885,7 +885,27 @@ export default ({ getService }: FtrProviderContext) => { .send(ruleUpdate) .expect(200); - expect(body.investigation_fields).to.eql(['foo', 'bar']); + expect(body.investigation_fields.field_names).to.eql(['foo', 'bar']); + }); + + it('should unset investigation_fields', async () => { + await createRule(supertest, log, { + ...getSimpleRule('rule-1'), + investigation_fields: { field_names: ['blob', 'boop'] }, + }); + + const ruleUpdate = { + ...getSimpleRuleUpdate('rule-1'), + investigation_fields: undefined, + }; + + const { body } = await supertest + .put(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send(ruleUpdate) + .expect(200); + + expect(body.investigation_fields).to.eql(undefined); }); }); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group6/alerts/alerts_compatibility.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group6/alerts/alerts_compatibility.ts index 1435565286485..9e7b6265a2b9f 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group6/alerts/alerts_compatibility.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group6/alerts/alerts_compatibility.ts @@ -337,7 +337,6 @@ export default ({ getService }: FtrProviderContext) => { max_signals: 100, risk_score_mapping: [], severity_mapping: [], - investigation_fields: [], threat: [], to: 'now', references: [], @@ -513,7 +512,6 @@ export default ({ getService }: FtrProviderContext) => { related_integrations: [], required_fields: [], setup: '', - investigation_fields: [], }, 'kibana.alert.rule.actions': [], 'kibana.alert.rule.created_by': 'elastic', diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/machine_learning.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/machine_learning.ts index b9f6b8caed951..792fcb30b6645 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/machine_learning.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/machine_learning.ts @@ -146,7 +146,6 @@ export default ({ getService }: FtrProviderContext) => { to: 'now', type: 'machine_learning', version: 1, - investigation_fields: [], }, [ALERT_DEPTH]: 1, [ALERT_REASON]: `event with process store, by root on mothra created critical alert Test ML rule.`, diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/new_terms.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/new_terms.ts index 72a71185065d9..0ac86c991015d 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/new_terms.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/new_terms.ts @@ -198,7 +198,6 @@ export default ({ getService }: FtrProviderContext) => { history_window_start: '2019-01-19T20:42:00.000Z', index: ['auditbeat-*'], language: 'kuery', - investigation_fields: [], }, 'kibana.alert.rule.actions': [], 'kibana.alert.rule.author': [], diff --git a/x-pack/test/detection_engine_api_integration/utils/get_complex_rule_output.ts b/x-pack/test/detection_engine_api_integration/utils/get_complex_rule_output.ts index 9c35d6652935f..0115b00c4b46b 100644 --- a/x-pack/test/detection_engine_api_integration/utils/get_complex_rule_output.ts +++ b/x-pack/test/detection_engine_api_integration/utils/get_complex_rule_output.ts @@ -102,5 +102,4 @@ export const getComplexRuleOutput = (ruleId = 'rule-1'): Partial = related_integrations: [], required_fields: [], setup: '', - investigation_fields: [], }); diff --git a/x-pack/test/detection_engine_api_integration/utils/get_rules_as_ndjson.ts b/x-pack/test/detection_engine_api_integration/utils/get_rules_as_ndjson.ts new file mode 100644 index 0000000000000..24d448af21f1b --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/utils/get_rules_as_ndjson.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/** + * Given an array of objects (assuming rules) this will return a ndjson buffer which is useful + * for testing uploads. + * @param rules Array of rules + */ +export const getRulesAsNdjson = (rules: unknown[]): Buffer => { + const stringOfRules = rules.map((rule) => { + return JSON.stringify(rule); + }); + return Buffer.from(stringOfRules.join('\n')); +}; diff --git a/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output.ts b/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output.ts index 92f427876e351..0a9eec4906a14 100644 --- a/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output.ts +++ b/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output.ts @@ -60,7 +60,7 @@ export const getMockSharedResponseSchema = ( timestamp_override: undefined, timestamp_override_fallback_disabled: undefined, namespace: undefined, - investigation_fields: [], + investigation_fields: undefined, }); const getQueryRuleOutput = (ruleId = 'rule-1', enabled = false): RuleResponse => ({ diff --git a/x-pack/test/detection_engine_api_integration/utils/index.ts b/x-pack/test/detection_engine_api_integration/utils/index.ts index dc41cedbca00e..4ad3ec1f1a62e 100644 --- a/x-pack/test/detection_engine_api_integration/utils/index.ts +++ b/x-pack/test/detection_engine_api_integration/utils/index.ts @@ -46,6 +46,7 @@ export * from './get_query_signal_ids'; export * from './get_query_signals_ids'; export * from './get_query_signals_rule_id'; export * from './get_rule'; +export * from './get_rules_as_ndjson'; export * from './get_rule_for_signal_testing'; export * from './get_rule_so_by_id'; export * from './get_rule_for_signal_testing_with_timestamp_override'; diff --git a/x-pack/test/functional/apps/infra/hosts_view.ts b/x-pack/test/functional/apps/infra/hosts_view.ts index 91270e7ebde58..a225ea4c476e4 100644 --- a/x-pack/test/functional/apps/infra/hosts_view.ts +++ b/x-pack/test/functional/apps/infra/hosts_view.ts @@ -459,6 +459,37 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); }); + describe('Host details page navigation', () => { + after(async () => { + await pageObjects.common.navigateToApp(HOSTS_VIEW_PATH); + await pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.timePicker.setAbsoluteRange( + START_DATE.format(DATE_PICKER_FORMAT), + END_DATE.format(DATE_PICKER_FORMAT) + ); + + await waitForPageToLoad(); + }); + + it('maintains selected date range when navigating to the individual host details', async () => { + const start = START_HOST_PROCESSES_DATE.format(DATE_PICKER_FORMAT); + const end = END_HOST_PROCESSES_DATE.format(DATE_PICKER_FORMAT); + + await pageObjects.timePicker.setAbsoluteRange(start, end); + + const hostDetailLinks = await pageObjects.infraHostsView.getAllHostDetailLinks(); + expect(hostDetailLinks.length).not.to.equal(0); + + await hostDetailLinks[0].click(); + + expect(await pageObjects.timePicker.timePickerExists()).to.be(true); + + const datePickerValue = await pageObjects.timePicker.getTimeConfig(); + expect(datePickerValue.start).to.equal(start); + expect(datePickerValue.end).to.equal(end); + }); + }); + describe('KPIs', () => { [ { metric: 'hostsCount', value: '6' }, diff --git a/x-pack/test/functional/apps/infra/node_details.ts b/x-pack/test/functional/apps/infra/node_details.ts index 7e2bdba6ecf39..d46d94121961c 100644 --- a/x-pack/test/functional/apps/infra/node_details.ts +++ b/x-pack/test/functional/apps/infra/node_details.ts @@ -51,6 +51,16 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { ); }; + const refreshPageWithDelay = async () => { + /** + * Delay gives React a chance to finish + * running effects (like updating the URL) before + * refreshing the page. + */ + await pageObjects.common.sleep(1000); + await browser.refresh(); + }; + describe('Node Details', () => { describe('#With Asset Details', () => { before(async () => { @@ -111,6 +121,19 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); } ); + + it('preserves selected date range between page reloads', async () => { + const start = moment.utc(START_HOST_ALERTS_DATE).format(DATE_PICKER_FORMAT); + const end = moment.utc(END_HOST_ALERTS_DATE).format(DATE_PICKER_FORMAT); + + await pageObjects.timePicker.setAbsoluteRange(start, end); + await refreshPageWithDelay(); + + const datePickerValue = await pageObjects.timePicker.getTimeConfig(); + + expect(datePickerValue.start).to.equal(start); + expect(datePickerValue.end).to.equal(end); + }); }); describe('#Asset Type: host', () => { @@ -121,6 +144,16 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { ); }); + it('preserves selected tab between page reloads', async () => { + await testSubjects.missingOrFail('infraAssetDetailsMetadataTable'); + await pageObjects.assetDetails.clickMetadataTab(); + await pageObjects.assetDetails.metadataTableExists(); + + await refreshPageWithDelay(); + + await pageObjects.assetDetails.metadataTableExists(); + }); + describe('Overview Tab', () => { before(async () => { await pageObjects.assetDetails.clickOverviewTab(); @@ -142,9 +175,9 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); }); - it('should render 8 charts in the Metrics section', async () => { + it('should render 12 charts in the Metrics section', async () => { const hosts = await pageObjects.assetDetails.getAssetDetailsMetricsCharts(); - expect(hosts.length).to.equal(8); + expect(hosts.length).to.equal(12); }); it('should show alerts', async () => { @@ -181,6 +214,20 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.assetDetails.clickRemoveMetadataPin(); expect(await pageObjects.assetDetails.metadataRemovePinExists()).to.be(false); }); + + it('preserves search term between page reloads', async () => { + const searchInput = await pageObjects.assetDetails.getMetadataSearchField(); + + expect(await searchInput.getAttribute('value')).to.be(''); + + await searchInput.type('test'); + await refreshPageWithDelay(); + + await retry.try(async () => { + expect(await searchInput.getAttribute('value')).to.be('test'); + }); + await searchInput.clearValue(); + }); }); describe('Processes Tab', () => { @@ -200,6 +247,20 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.assetDetails.getProcessesTableBody(); await pageObjects.assetDetails.clickProcessesTableExpandButton(); }); + + it('preserves search term between page reloads', async () => { + const searchInput = await pageObjects.assetDetails.getProcessesSearchField(); + + expect(await searchInput.getAttribute('value')).to.be(''); + + await searchInput.type('test'); + await refreshPageWithDelay(); + + await retry.try(async () => { + expect(await searchInput.getAttribute('value')).to.be('test'); + }); + await searchInput.clearValue(); + }); }); describe('Logs Tab', () => { @@ -210,6 +271,20 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { it('should render logs tab', async () => { await testSubjects.existOrFail('infraAssetDetailsLogsTabContent'); }); + + it('preserves search term between page reloads', async () => { + const searchInput = await pageObjects.assetDetails.getLogsSearchField(); + + expect(await searchInput.getAttribute('value')).to.be(''); + + await searchInput.type('test'); + await refreshPageWithDelay(); + + await retry.try(async () => { + expect(await searchInput.getAttribute('value')).to.be('test'); + }); + await searchInput.clearValue(); + }); }); describe('Osquery Tab', () => { diff --git a/x-pack/test/functional/apps/transform/creation/index_pattern/continuous_transform.ts b/x-pack/test/functional/apps/transform/creation/index_pattern/continuous_transform.ts index 0fed167afb835..a284070fa7b6b 100644 --- a/x-pack/test/functional/apps/transform/creation/index_pattern/continuous_transform.ts +++ b/x-pack/test/functional/apps/transform/creation/index_pattern/continuous_transform.ts @@ -7,7 +7,6 @@ import { TRANSFORM_STATE } from '@kbn/transform-plugin/common/constants'; -import type { MappingTypeMapping } from '@elastic/elasticsearch/lib/api/types'; import type { FtrProviderContext } from '../../../../ftr_provider_context'; import { GroupByEntry, @@ -231,13 +230,91 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { if (testData.type === 'latest') { const destIndexMappings: MappingTypeMapping = { properties: { + customer_id: { + type: 'long', + }, + day_of_week_i: { + type: 'long', + }, + order_date: { + type: 'date', + }, + order_id: { + type: 'long', + }, products: { properties: { + _id: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + ignore_above: 256, + }, + }, + }, base_price: { type: 'float', }, + base_unit_price: { + type: 'float', + }, + created_on: { + type: 'date', + }, + discount_amount: { + type: 'float', + }, + discount_percentage: { + type: 'float', + }, + min_price: { + type: 'float', + }, + price: { + type: 'float', + }, + product_id: { + type: 'long', + }, + product_name: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + ignore_above: 256, + }, + }, + }, + quantity: { + type: 'long', + }, + tax_amount: { + type: 'float', + }, + taxful_price: { + type: 'float', + }, + taxless_price: { + type: 'float', + }, + unit_discount_amount: { + type: 'float', + }, }, }, + taxful_total_price: { + type: 'float', + }, + taxless_total_price: { + type: 'float', + }, + total_quantity: { + type: 'long', + }, + total_unique_products: { + type: 'long', + }, }, }; diff --git a/x-pack/test/functional/page_objects/api_keys_page.ts b/x-pack/test/functional/page_objects/api_keys_page.ts index 23f63c87107fa..0875774470018 100644 --- a/x-pack/test/functional/page_objects/api_keys_page.ts +++ b/x-pack/test/functional/page_objects/api_keys_page.ts @@ -66,7 +66,7 @@ export function ApiKeysPageProvider({ getService }: FtrProviderContext) { }, async isApiKeyModalExists() { - return await find.existsByCssSelector('[role="dialog"]'); + return await find.existsByCssSelector('.euiFlyoutHeader'); }, async getNewApiKeyCreation() { diff --git a/x-pack/test/functional/page_objects/asset_details.ts b/x-pack/test/functional/page_objects/asset_details.ts index bcb8738b997bf..c7600d6f1b5f4 100644 --- a/x-pack/test/functional/page_objects/asset_details.ts +++ b/x-pack/test/functional/page_objects/asset_details.ts @@ -77,6 +77,10 @@ export function AssetDetailsProvider({ getService }: FtrProviderContext) { return testSubjects.existOrFail('infraAssetDetailsMetadataTable'); }, + async metadataTableMissing() { + return await testSubjects.missingOrFail('infraAssetDetailsMetadataTable'); + }, + async metadataRemovePinExists() { return testSubjects.exists('infraAssetDetailsMetadataRemovePin'); }, @@ -92,6 +96,10 @@ export function AssetDetailsProvider({ getService }: FtrProviderContext) { return testSubjects.exists('infraAssetDetailsMetadataRemoveFilterButton'); }, + async getMetadataSearchField() { + return await testSubjects.find('infraAssetDetailsMetadataSearchBarInput'); + }, + // Processes async clickProcessesTab() { return testSubjects.click('infraAssetDetailsProcessesTab'); @@ -124,6 +132,10 @@ export function AssetDetailsProvider({ getService }: FtrProviderContext) { return testSubjects.click('infraProcessRowButton'); }, + async getProcessesSearchField() { + return await testSubjects.find('infraAssetDetailsProcessesSearchBarInput'); + }, + // Logs async clickLogsTab() { return testSubjects.click('infraAssetDetailsLogsTab'); @@ -133,6 +145,10 @@ export function AssetDetailsProvider({ getService }: FtrProviderContext) { await testSubjects.existOrFail('infraAssetDetailsLogsTabContent'); }, + async getLogsSearchField() { + return await testSubjects.find('infraAssetDetailsLogsTabFieldSearch'); + }, + // Anomalies async clickAnomaliesTab() { return testSubjects.click('infraAssetDetailsAnomaliesTab'); diff --git a/x-pack/test/functional/page_objects/infra_hosts_view.ts b/x-pack/test/functional/page_objects/infra_hosts_view.ts index e449fb8f05b57..1cd0cf15996ec 100644 --- a/x-pack/test/functional/page_objects/infra_hosts_view.ts +++ b/x-pack/test/functional/page_objects/infra_hosts_view.ts @@ -110,6 +110,10 @@ export function InfraHostsViewProvider({ getService }: FtrProviderContext) { return testSubjects.find('hostsView-metricChart'); }, + async getAllHostDetailLinks() { + return testSubjects.findAll('hostsViewTableEntryTitleLink'); + }, + // Metrics Tab async getMetricsTab() { return testSubjects.find('hostsView-tabs-metrics'); diff --git a/x-pack/test/security_solution_cypress/config.ts b/x-pack/test/security_solution_cypress/config.ts index c50a5403a166c..040f92eb7945e 100644 --- a/x-pack/test/security_solution_cypress/config.ts +++ b/x-pack/test/security_solution_cypress/config.ts @@ -49,6 +49,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { `--xpack.securitySolution.enableExperimental=${JSON.stringify([ 'alertDetailsPageEnabled', 'chartEmbeddablesEnabled', + 'discoverInTimeline', ])}`, // mock cloud to enable the guided onboarding tour in e2e tests '--xpack.cloud.id=test', diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts/alert_tags.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts/alert_tags.cy.ts index 39758e5a097c8..52a6afc2e4f25 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts/alert_tags.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts/alert_tags.cy.ts @@ -33,7 +33,7 @@ describe('Alert tagging', { tags: ['@ess', '@serverless'] }, () => { beforeEach(() => { login(); deleteAlertsAndRules(); - cy.task('esArchiverLoad', 'endpoint'); + cy.task('esArchiverLoad', { archiveName: 'endpoint' }); createRule(getNewRule({ rule_id: 'new custom rule' })); visit(ALERTS_URL); waitForAlertsToPopulate(); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts/cti_enrichments.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts/cti_enrichments.cy.ts index 27921bb9b2d70..ebf5f97bbc790 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts/cti_enrichments.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts/cti_enrichments.cy.ts @@ -31,8 +31,8 @@ import { addsFieldsToTimeline } from '../../tasks/rule_details'; describe('CTI Enrichment', { tags: ['@ess', '@serverless'] }, () => { before(() => { cleanKibana(); - cy.task('esArchiverLoad', 'threat_indicator'); - cy.task('esArchiverLoad', 'suspicious_source_event'); + cy.task('esArchiverLoad', { archiveName: 'threat_indicator' }); + cy.task('esArchiverLoad', { archiveName: 'suspicious_source_event' }); login(); createRule({ ...getNewThreatIndicatorRule(), rule_id: 'rule_testing', enabled: true }); disableExpandableFlyout(); @@ -155,7 +155,7 @@ describe('CTI Enrichment', { tags: ['@ess', '@serverless'] }, () => { describe('with additional indicators', () => { before(() => { - cy.task('esArchiverLoad', 'threat_indicator2'); + cy.task('esArchiverLoad', { archiveName: 'threat_indicator2' }); }); beforeEach(() => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts/enrichments.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts/enrichments.cy.ts index 3cf0665795cf6..d427a7e3d43a4 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts/enrichments.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts/enrichments.cy.ts @@ -33,7 +33,7 @@ import { ALERTS_URL } from '../../urls/navigation'; describe('Enrichment', { tags: ['@ess', '@serverless'] }, () => { before(() => { cleanKibana(); - cy.task('esArchiverLoad', 'risk_users'); + cy.task('esArchiverLoad', { archiveName: 'risk_users' }); }); after(() => { @@ -43,7 +43,7 @@ describe('Enrichment', { tags: ['@ess', '@serverless'] }, () => { describe('Custom query rule', () => { beforeEach(() => { disableExpandableFlyout(); - cy.task('esArchiverLoad', 'risk_hosts'); + cy.task('esArchiverLoad', { archiveName: 'risk_hosts' }); deleteAlertsAndRules(); createRule(getNewRule({ rule_id: 'rule1' })); login(); @@ -75,7 +75,7 @@ describe('Enrichment', { tags: ['@ess', '@serverless'] }, () => { closeAlertFlyout(); cy.task('esArchiverUnload', 'risk_hosts'); - cy.task('esArchiverLoad', 'risk_hosts_updated'); + cy.task('esArchiverLoad', { archiveName: 'risk_hosts_updated' }); expandFirstAlert(); cy.get(ENRICHED_DATA_ROW).contains('Critical'); cy.get(ENRICHED_DATA_ROW).contains('Original host risk classification'); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts/ransomware_detection.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts/ransomware_detection.cy.ts index 93e0ce8ba8f4e..236a23da8e71d 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts/ransomware_detection.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts/ransomware_detection.cy.ts @@ -16,7 +16,9 @@ import { createTimeline } from '../../tasks/timelines'; describe('Ransomware Detection Alerts', { tags: ['@ess', '@serverless'] }, () => { before(() => { - cy.task('esArchiverLoad', 'ransomware_detection'); + cy.task('esArchiverLoad', { + archiveName: 'ransomware_detection', + }); }); describe('Ransomware display in Alerts Section', () => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts/ransomware_prevention.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts/ransomware_prevention.cy.ts index 4f6379b3a6bd9..ea80743898686 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts/ransomware_prevention.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts/ransomware_prevention.cy.ts @@ -13,10 +13,14 @@ import { ALERTS_HISTOGRAM_SERIES, ALERT_RULE_NAME, MESSAGE } from '../../screens import { TIMELINE_QUERY, TIMELINE_VIEW_IN_ANALYZER } from '../../screens/timeline'; import { selectAlertsHistogram } from '../../tasks/alerts'; import { createTimeline } from '../../tasks/timelines'; +import { cleanKibana } from '../../tasks/common'; describe('Ransomware Prevention Alerts', { tags: ['@ess', '@serverless'] }, () => { before(() => { - cy.task('esArchiverLoad', 'ransomware_prevention'); + cleanKibana(); + cy.task('esArchiverLoad', { + archiveName: 'ransomware_prevention', + }); }); after(() => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/event_correlation_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/event_correlation_rule.cy.ts index 73e501009656a..a767fe7b533e2 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/event_correlation_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/event_correlation_rule.cy.ts @@ -151,7 +151,7 @@ describe('EQL rules', { tags: ['@ess', '@brokenInServerless'] }, () => { const rule = getEqlSequenceRule(); beforeEach(() => { - cy.task('esArchiverLoad', 'auditbeat_big'); + cy.task('esArchiverLoad', { archiveName: 'auditbeat_big' }); }); afterEach(() => { cy.task('esArchiverUnload', 'auditbeat_big'); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/indicator_match_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/indicator_match_rule.cy.ts index b78d67d58980a..6174d07e8ad5d 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/indicator_match_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/indicator_match_rule.cy.ts @@ -120,8 +120,8 @@ describe('indicator match', { tags: ['@ess', '@brokenInServerless'] }, () => { before(() => { cleanKibana(); - cy.task('esArchiverLoad', 'threat_indicator'); - cy.task('esArchiverLoad', 'suspicious_source_event'); + cy.task('esArchiverLoad', { archiveName: 'threat_indicator' }); + cy.task('esArchiverLoad', { archiveName: 'suspicious_source_event' }); }); beforeEach(() => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/entity_analytics_management_page.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/entity_analytics_management_page.cy.ts index ae83f8b769b17..36bc18210863f 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/entity_analytics_management_page.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/entity_analytics_management_page.cy.ts @@ -50,7 +50,7 @@ describe( () => { before(() => { cleanKibana(); - cy.task('esArchiverLoad', 'all_users'); + cy.task('esArchiverLoad', { archiveName: 'all_users' }); }); beforeEach(() => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/endpoint_exceptions.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/endpoint_exceptions.cy.ts index 05dfb695956b5..920ddd62909b3 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/endpoint_exceptions.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/endpoint_exceptions.cy.ts @@ -54,7 +54,7 @@ describe.skip( cy.task('esArchiverResetKibana'); login(); deleteAlertsAndRules(); - cy.task('esArchiverLoad', 'endpoint'); + cy.task('esArchiverLoad', { archiveName: 'endpoint' }); createRule(getEndpointRule()); visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL); goToRuleDetails(); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/rule_exceptions/auto_populate_with_alert_data.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/rule_exceptions/auto_populate_with_alert_data.cy.ts index 82cfc48100402..14d002b96b7b4 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/rule_exceptions/auto_populate_with_alert_data.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/rule_exceptions/auto_populate_with_alert_data.cy.ts @@ -50,7 +50,7 @@ describe.skip( beforeEach(() => { cy.task('esArchiverUnload', 'endpoint'); cy.task('esArchiverResetKibana'); - cy.task('esArchiverLoad', 'endpoint'); + cy.task('esArchiverLoad', { archiveName: 'endpoint' }); login(); createRule(getEndpointRule()); visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/rule_exceptions/closing_all_matching_alerts.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/rule_exceptions/closing_all_matching_alerts.cy.ts index ea905a7774126..9c4a575dccb4e 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/rule_exceptions/closing_all_matching_alerts.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/rule_exceptions/closing_all_matching_alerts.cy.ts @@ -36,7 +36,7 @@ describe('Close matching Alerts ', () => { cy.task('esArchiverUnload', 'exceptions'); cy.task('esArchiverResetKibana'); deleteAlertsAndRules(); - cy.task('esArchiverLoad', 'exceptions'); + cy.task('esArchiverLoad', { archiveName: 'exceptions' }); login(); postDataView('exceptions-*'); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/entry/flyout_validation.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/entry/flyout_validation.cy.ts index 078f743e1cea7..e11cace87d8e8 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/entry/flyout_validation.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/entry/flyout_validation.cy.ts @@ -71,10 +71,10 @@ describe.skip('Exceptions flyout', { tags: ['@ess', '@serverless'] }, () => { // this is a made-up index that has just the necessary // mappings to conduct tests, avoiding loading large // amounts of data like in auditbeat_exceptions - cy.task('esArchiverLoad', 'exceptions'); + cy.task('esArchiverLoad', { archiveName: 'exceptions' }); // Comment the Conflicts here as they are skipped - // cy.task('esArchiverLoad', 'conflicts_1'); - // cy.task('esArchiverLoad', 'conflicts_2'); + // cy.task('esArchiverLoad',{ archiveName: 'conflicts_1' }); + // cy.task('esArchiverLoad',{ archiveName: 'conflicts_2' }); login(); createExceptionList(getExceptionList(), getExceptionList().list_id).then((response) => createRule( diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/entry/use_value_list.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/entry/use_value_list.cy.ts index 51f0e954f63ce..e45484e5fb834 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/entry/use_value_list.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/entry/use_value_list.cy.ts @@ -54,7 +54,7 @@ describe('Use Value list in exception entry', { tags: ['@ess', '@serverless'] }, before(() => { cleanKibana(); login(); - cy.task('esArchiverLoad', 'exceptions'); + cy.task('esArchiverLoad', { archiveName: 'exceptions' }); createRule({ ...getNewRule(), query: 'user.name:*', diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_endpoint_exception.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_endpoint_exception.cy.ts index 47f6717fa3143..cdf0b56347063 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_endpoint_exception.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_endpoint_exception.cy.ts @@ -49,7 +49,7 @@ describe('Add endpoint exception from rule details', { tags: ['@ess', '@serverle before(() => { cy.task('esArchiverResetKibana'); - cy.task('esArchiverLoad', 'auditbeat'); + cy.task('esArchiverLoad', { archiveName: 'auditbeat' }); login(); deleteAlertsAndRules(); // create rule with exception diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_exception.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_exception.cy.ts index 6a51085df468b..18537145ec582 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_exception.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_exception.cy.ts @@ -66,7 +66,7 @@ describe('Add/edit exception from rule details', { tags: ['@ess', '@brokenInServ before(() => { cy.task('esArchiverResetKibana'); - cy.task('esArchiverLoad', 'exceptions'); + cy.task('esArchiverLoad', { archiveName: 'exceptions' }); login(); }); @@ -319,7 +319,7 @@ describe('Add/edit exception from rule details', { tags: ['@ess', '@brokenInServ cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('exist'); // load more docs - cy.task('esArchiverLoad', 'exceptions_2'); + cy.task('esArchiverLoad', { archiveName: 'exceptions_2' }); // now that there are no more exceptions, the docs should match and populate alerts goToAlertsTab(); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_exception_data_view.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_exception_data_view.cy.ts index ada21eb844450..229cfce25b9fa 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_exception_data_view.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_exception_data_view.cy.ts @@ -50,7 +50,7 @@ describe( before(() => { cy.task('esArchiverResetKibana'); - cy.task('esArchiverLoad', 'exceptions'); + cy.task('esArchiverLoad', { archiveName: 'exceptions' }); login(); postDataView('exceptions-*'); }); @@ -117,7 +117,7 @@ describe( cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('exist'); // load more docs - cy.task('esArchiverLoad', 'exceptions_2'); + cy.task('esArchiverLoad', { archiveName: 'exceptions_2' }); // now that there are no more exceptions, the docs should match and populate alerts goToAlertsTab(); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/manage_exceptions.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/manage_exceptions.cy.ts index 7f343b46a3467..5c9b93ed1507b 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/manage_exceptions.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/manage_exceptions.cy.ts @@ -41,7 +41,7 @@ import { describe('Add, edit and delete exception', { tags: ['@ess', '@serverless'] }, () => { before(() => { cy.task('esArchiverResetKibana'); - cy.task('esArchiverLoad', 'exceptions'); + cy.task('esArchiverLoad', { archiveName: 'exceptions' }); createRule(getNewRule()); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/explore/dashboards/entity_analytics.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/dashboards/entity_analytics.cy.ts index cea38567dbf70..07c14cf9f5e8f 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/explore/dashboards/entity_analytics.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/dashboards/entity_analytics.cy.ts @@ -77,8 +77,8 @@ describe('Entity Analytics Dashboard', { tags: ['@ess', '@brokenInServerless'] } describe('Risk Score enabled but still no data', () => { before(() => { - cy.task('esArchiverLoad', 'risk_hosts_no_data'); - cy.task('esArchiverLoad', 'risk_users_no_data'); + cy.task('esArchiverLoad', { archiveName: 'risk_hosts_no_data' }); + cy.task('esArchiverLoad', { archiveName: 'risk_users_no_data' }); }); beforeEach(() => { @@ -102,8 +102,8 @@ describe('Entity Analytics Dashboard', { tags: ['@ess', '@brokenInServerless'] } describe('With Legacy data', () => { before(() => { - cy.task('esArchiverLoad', 'risk_hosts_legacy_data'); - cy.task('esArchiverLoad', 'risk_users_legacy_data'); + cy.task('esArchiverLoad', { archiveName: 'risk_hosts_legacy_data' }); + cy.task('esArchiverLoad', { archiveName: 'risk_users_legacy_data' }); }); beforeEach(() => { @@ -127,7 +127,7 @@ describe('Entity Analytics Dashboard', { tags: ['@ess', '@brokenInServerless'] } describe('With host risk data', () => { before(() => { - cy.task('esArchiverLoad', 'risk_hosts'); + cy.task('esArchiverLoad', { archiveName: 'risk_hosts' }); }); beforeEach(() => { @@ -215,7 +215,7 @@ describe('Entity Analytics Dashboard', { tags: ['@ess', '@brokenInServerless'] } describe('With user risk data', () => { before(() => { - cy.task('esArchiverLoad', 'risk_users'); + cy.task('esArchiverLoad', { archiveName: 'risk_users' }); }); beforeEach(() => { @@ -304,7 +304,7 @@ describe('Entity Analytics Dashboard', { tags: ['@ess', '@brokenInServerless'] } describe('With anomalies data', () => { before(() => { - cy.task('esArchiverLoad', 'network'); + cy.task('esArchiverLoad', { archiveName: 'network' }); login(); visit(ENTITY_ANALYTICS_URL); cy.get(ANOMALIES_TABLE).should('be.visible'); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/explore/host_details/risk_tab.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/host_details/risk_tab.cy.ts index cdfd01c034c33..aed7de2110ba6 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/explore/host_details/risk_tab.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/host_details/risk_tab.cy.ts @@ -13,7 +13,7 @@ import { TABLE_CELL, TABLE_ROWS } from '../../../screens/alerts_details'; describe('risk tab', { tags: ['@ess', '@serverless'] }, () => { before(() => { cleanKibana(); - cy.task('esArchiverLoad', 'risk_hosts'); + cy.task('esArchiverLoad', { archiveName: 'risk_hosts' }); }); beforeEach(() => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/explore/hosts/events_viewer.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/hosts/events_viewer.cy.ts index 95976d90f9557..d8115e40cbb20 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/explore/hosts/events_viewer.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/hosts/events_viewer.cy.ts @@ -47,7 +47,7 @@ const defaultHeadersInDefaultEcsCategory = [ describe('Events Viewer', { tags: ['@ess', '@brokenInServerless'] }, () => { before(() => { - cy.task('esArchiverLoad', 'auditbeat_big'); + cy.task('esArchiverLoad', { archiveName: 'auditbeat_big' }); }); after(() => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/explore/hosts/host_risk_tab.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/hosts/host_risk_tab.cy.ts index ce59d0de0b9ad..1a64f741ebeff 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/explore/hosts/host_risk_tab.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/hosts/host_risk_tab.cy.ts @@ -24,7 +24,7 @@ import { clearSearchBar, kqlSearch } from '../../../tasks/security_header'; describe('risk tab', { tags: ['@ess', '@brokenInServerless'] }, () => { before(() => { cleanKibana(); - cy.task('esArchiverLoad', 'risk_hosts'); + cy.task('esArchiverLoad', { archiveName: 'risk_hosts' }); }); beforeEach(() => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/explore/hosts/hosts_risk_column.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/hosts/hosts_risk_column.cy.ts index 664e5767dc2b9..77593f429b1d4 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/explore/hosts/hosts_risk_column.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/hosts/hosts_risk_column.cy.ts @@ -15,7 +15,7 @@ import { kqlSearch } from '../../../tasks/security_header'; describe('All hosts table', { tags: ['@ess', '@serverless'] }, () => { before(() => { cleanKibana(); - cy.task('esArchiverLoad', 'risk_hosts'); + cy.task('esArchiverLoad', { archiveName: 'risk_hosts' }); }); beforeEach(() => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/explore/network/hover_actions.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/network/hover_actions.cy.ts index 036fd908a8f0d..4f634ebc3ce2f 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/explore/network/hover_actions.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/network/hover_actions.cy.ts @@ -32,7 +32,7 @@ describe.skip('Hover actions', { tags: ['@ess', '@serverless'] }, () => { }; before(() => { - cy.task('esArchiverLoad', 'network'); + cy.task('esArchiverLoad', { archiveName: 'network' }); }); after(() => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/explore/network/overflow_items.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/network/overflow_items.cy.ts index 71bdd66b9b381..9ac413d75a6d2 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/explore/network/overflow_items.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/network/overflow_items.cy.ts @@ -25,7 +25,7 @@ const testDomainTwo = 'myTest2'; describe('Overflow items', { tags: ['@ess', '@serverless'] }, () => { context('Network stats and tables', () => { before(() => { - cy.task('esArchiverLoad', 'network'); + cy.task('esArchiverLoad', { archiveName: 'network' }); }); beforeEach(() => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/explore/overview/overview.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/overview/overview.cy.ts index eec86830718a7..514569c2c1667 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/explore/overview/overview.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/overview/overview.cy.ts @@ -19,7 +19,7 @@ import { getTimeline } from '../../../objects/timeline'; describe('Overview Page', { tags: ['@ess', '@serverless'] }, () => { before(() => { cleanKibana(); - cy.task('esArchiverLoad', 'overview'); + cy.task('esArchiverLoad', { archiveName: 'overview' }); }); beforeEach(() => { @@ -69,7 +69,7 @@ describe('Overview page with no data', { tags: '@brokenInServerless' }, () => { cy.task('esArchiverUnload', 'auditbeat'); }); after(() => { - cy.task('esArchiverLoad', 'auditbeat'); + cy.task('esArchiverLoad', { archiveName: 'auditbeat' }); }); it('Splash screen should be here', () => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/explore/pagination/pagination.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/pagination/pagination.cy.ts index 3f9b31b819c42..1e7f7d7514404 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/explore/pagination/pagination.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/pagination/pagination.cy.ts @@ -23,7 +23,7 @@ import { goToTablePage, sortFirstTableColumn } from '../../../tasks/table_pagina describe('Pagination', { tags: ['@ess', '@serverless'] }, () => { describe('Host uncommon processes table)', () => { before(() => { - cy.task('esArchiverLoad', 'host_uncommon_processes'); + cy.task('esArchiverLoad', { archiveName: 'host_uncommon_processes' }); }); beforeEach(() => { @@ -99,7 +99,7 @@ describe('Pagination', { tags: ['@ess', '@serverless'] }, () => { describe('All users and all Hosts tables', () => { before(() => { - cy.task('esArchiverLoad', 'all_users'); + cy.task('esArchiverLoad', { archiveName: 'all_users' }); }); beforeEach(() => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/explore/users/users_tabs.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/users/users_tabs.cy.ts index 34401c1743cd9..5b655be068ea9 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/explore/users/users_tabs.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/users/users_tabs.cy.ts @@ -22,9 +22,9 @@ import { USERS_URL } from '../../../urls/navigation'; describe('Users stats and tables', () => { before(() => { cleanKibana(); - cy.task('esArchiverLoad', 'users'); + cy.task('esArchiverLoad', { archiveName: 'users' }); - cy.task('esArchiverLoad', 'risk_users'); + cy.task('esArchiverLoad', { archiveName: 'risk_users' }); }); beforeEach(() => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/inspect/inspect_button.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/inspect/inspect_button.cy.ts index 209422bcf7eb7..fd9fc56c95fc4 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/inspect/inspect_button.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/inspect/inspect_button.cy.ts @@ -24,8 +24,8 @@ const DATA_VIEW = 'auditbeat-*'; describe('Inspect Explore pages', { tags: ['@ess', '@serverless'] }, () => { before(() => { - cy.task('esArchiverLoad', 'risk_users'); - cy.task('esArchiverLoad', 'risk_hosts'); + cy.task('esArchiverLoad', { archiveName: 'risk_users' }); + cy.task('esArchiverLoad', { archiveName: 'risk_hosts' }); login(); // Create and select data view diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/alert_table_action_column.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/alert_table_action_column.cy.ts index a8acec0019db6..975fdb3ee1d59 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/alert_table_action_column.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/alert_table_action_column.cy.ts @@ -18,7 +18,9 @@ import { ALERTS_URL } from '../../../urls/navigation'; describe('Alerts Table Action column', { tags: ['@ess', '@serverless'] }, () => { before(() => { cleanKibana(); - cy.task('esArchiverLoad', 'process_ancestry'); + cy.task('esArchiverLoad', { + archiveName: 'process_ancestry', + }); }); beforeEach(() => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/alerts_details.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/alerts_details.cy.ts index d4ecaf16df975..61bfbc8ee0958 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/alerts_details.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/alerts_details.cy.ts @@ -13,6 +13,7 @@ import { COPY_ALERT_FLYOUT_LINK, JSON_TEXT, OVERVIEW_RULE, + SUMMARY_VIEW, TABLE_CONTAINER, TABLE_ROWS, } from '../../../screens/alerts_details'; @@ -37,7 +38,7 @@ import { goToRuleDetails } from '../../../tasks/alerts_detection_rules'; describe('Alert details flyout', { tags: ['@ess', '@serverless'] }, () => { describe('Basic functions', () => { - before(() => { + beforeEach(() => { cleanKibana(); login(); disableExpandableFlyout(); @@ -48,6 +49,7 @@ describe('Alert details flyout', { tags: ['@ess', '@serverless'] }, () => { }); it('should update the table when status of the alert is updated', () => { + cy.get(OVERVIEW_RULE).should('be.visible'); cy.get(ALERTS_TABLE_COUNT).should('have.text', '2 alerts'); cy.get(ALERT_SUMMARY_SEVERITY_DONUT_CHART).should('contain.text', '2alerts'); expandFirstAlert(); @@ -60,8 +62,8 @@ describe('Alert details flyout', { tags: ['@ess', '@serverless'] }, () => { describe('With unmapped fields', () => { before(() => { cleanKibana(); - cy.task('esArchiverLoad', 'unmapped_fields'); - createRule(getUnmappedRule()); + cy.task('esArchiverLoad', { archiveName: 'unmapped_fields' }); + createRule({ ...getUnmappedRule(), investigation_fields: { field_names: ['event.kind'] } }); }); beforeEach(() => { @@ -76,6 +78,13 @@ describe('Alert details flyout', { tags: ['@ess', '@serverless'] }, () => { cy.task('esArchiverUnload', 'unmapped_fields'); }); + it('should display user and system defined highlighted fields', () => { + cy.get(SUMMARY_VIEW) + .should('be.visible') + .and('contain.text', 'event.kind') + .and('contain.text', 'Rule type'); + }); + it('should display the unmapped field on the JSON view', () => { const expectedUnmappedValue = 'This is the unmapped field'; @@ -126,7 +135,7 @@ describe('Alert details flyout', { tags: ['@ess', '@serverless'] }, () => { describe('Url state management', () => { before(() => { cleanKibana(); - cy.task('esArchiverLoad', 'query_alert'); + cy.task('esArchiverLoad', { archiveName: 'query_alert' }); }); beforeEach(() => { @@ -172,7 +181,7 @@ describe('Alert details flyout', { tags: ['@ess', '@serverless'] }, () => { describe('Localstorage management', () => { before(() => { cleanKibana(); - cy.task('esArchiverLoad', 'query_alert'); + cy.task('esArchiverLoad', { archiveName: 'query_alert' }); }); beforeEach(() => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/building_block_alerts.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/building_block_alerts.cy.ts index 6d3dc872e9c3c..0ed530adad451 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/building_block_alerts.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/building_block_alerts.cy.ts @@ -27,7 +27,7 @@ describe( { tags: ['@ess', '@brokenInServerless'] }, () => { before(() => { - cy.task('esArchiverLoad', 'auditbeat_big'); + cy.task('esArchiverLoad', { archiveName: 'auditbeat_big' }); cleanKibana(); login(); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/changing_alert_status.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/changing_alert_status.cy.ts index 6eb08bb5930a0..d7aa5040072cc 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/changing_alert_status.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/changing_alert_status.cy.ts @@ -40,7 +40,7 @@ import { ALERTS_URL } from '../../../urls/navigation'; describe('Changing alert status', { tags: ['@ess', '@brokenInServerless'] }, () => { before(() => { - cy.task('esArchiverLoad', 'auditbeat_big'); + cy.task('esArchiverLoad', { archiveName: 'auditbeat_big' }); cleanKibana(); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_left_panel_prevalence_tab.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_left_panel_prevalence_tab.cy.ts index baff17ce85d03..4370acb6acd99 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_left_panel_prevalence_tab.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_left_panel_prevalence_tab.cy.ts @@ -35,7 +35,7 @@ describe('Alert details expandable flyout left panel prevalence', () => { beforeEach(() => { cleanKibana(); login(); - createRule(getNewRule()); + createRule({ ...getNewRule(), investigation_fields: { field_names: ['host.os.name'] } }); visit(ALERTS_URL); waitForAlertsToPopulate(); expandFirstAlertExpandableFlyout(); @@ -57,10 +57,12 @@ describe('Alert details expandable flyout left panel prevalence', () => { cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_PREVALENCE_TABLE).should('be.visible'); cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_PREVALENCE_TABLE_TYPE_CELL) - .should('contain.text', 'host.name') + .should('contain.text', 'host.os.name') + .and('contain.text', 'host.name') .and('contain.text', 'user.name'); cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_PREVALENCE_TABLE_NAME_CELL) - .should('contain.text', 'siem-kibana') + .should('contain.text', 'Mac OS X') + .and('contain.text', 'siem-kibana') .and('contain.text', 'test'); cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_PREVALENCE_TABLE_ALERT_COUNT_CELL).should( 'contain.text', @@ -68,7 +70,7 @@ describe('Alert details expandable flyout left panel prevalence', () => { ); cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_PREVALENCE_TABLE_DOC_COUNT_CELL).should( 'contain.text', - 0 + '—' ); cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_PREVALENCE_TABLE_HOST_PREVALENCE_CELL).should( 'contain.text', diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts index 6c9f14cbc7a04..dfd09cf4ca117 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts @@ -71,7 +71,7 @@ describe( 'Alert details expandable flyout right panel overview tab', { tags: ['@ess', '@brokenInServerless'] }, () => { - const rule = getNewRule(); + const rule = { ...getNewRule(), investigation_fields: { field_names: ['host.os.name'] } }; beforeEach(() => { cleanKibana(); @@ -177,7 +177,7 @@ describe( cy.get(DOCUMENT_DETAILS_FLYOUT_INVESTIGATION_TAB_CONTENT).scrollIntoView(); cy.get(DOCUMENT_DETAILS_FLYOUT_INVESTIGATION_TAB_CONTENT).should('be.visible'); - cy.log('highlighted fields'); + cy.log('highlighted fields section'); cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_HIGHLIGHTED_FIELDS_HEADER_TITLE) .should('be.visible') @@ -186,6 +186,17 @@ describe( 'be.visible' ); + cy.log('custom highlighted fields'); + + cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_HIGHLIGHTED_FIELDS_TABLE_FIELD_CELL) + .should('be.visible') + .and('contain.text', 'host.os.name'); + const customHighlightedField = + DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_HIGHLIGHTED_FIELDS_TABLE_VALUE_CELL('Mac OS X'); + cy.get(customHighlightedField).should('be.visible').and('have.text', 'Mac OS X'); + + cy.log('system defined highlighted fields'); + cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_HIGHLIGHTED_FIELDS_TABLE_FIELD_CELL) .should('be.visible') .and('contain.text', 'host.name'); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/bulk_add_to_timeline.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/bulk_add_to_timeline.cy.ts index 3470c21aa0ed3..3ba223097023a 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/bulk_add_to_timeline.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/bulk_add_to_timeline.cy.ts @@ -25,7 +25,7 @@ import { ALERTS_URL, HOSTS_URL } from '../../../urls/navigation'; describe('Bulk Investigate in Timeline', { tags: ['@ess', '@serverless'] }, () => { before(() => { cleanKibana(); - cy.task('esArchiverLoad', 'bulk_process'); + cy.task('esArchiverLoad', { archiveName: 'bulk_process' }); login(); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/cell_actions.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/cell_actions.cy.ts new file mode 100644 index 0000000000000..7eb818ef9205f --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/cell_actions.cy.ts @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { grantClipboardReadPerm } from '../../../../tasks/common/clipboard'; +import { + DISCOVER_CELL_ACTIONS, + DISCOVER_CONTAINER, + DISCOVER_FILTER_BADGES, + GET_DISCOVER_DATA_GRID_CELL, +} from '../../../../screens/discover'; +import { waitForDiscoverGridToLoad } from '../../../../tasks/discover'; +import { updateDateRangeInLocalDatePickers } from '../../../../tasks/date_picker'; +import { login, visit } from '../../../../tasks/login'; +import { createNewTimeline, gotToDiscoverTab } from '../../../../tasks/timeline'; +import { ALERTS_URL } from '../../../../urls/navigation'; + +const INITIAL_START_DATE = 'Jan 18, 2021 @ 20:33:29.186'; +const INITIAL_END_DATE = 'Jan 19, 2024 @ 20:33:29.186'; +const TIMESTAMP_COLUMN_NAME = '@timestamp'; + +describe( + `Discover Datagrid Cell Actions`, + { + env: { ftrConfig: { enableExperimental: ['discoverInTimeline'] } }, + tags: ['@ess', '@serverless'], + }, + () => { + beforeEach(() => { + login(); + visit(ALERTS_URL); + createNewTimeline(); + gotToDiscoverTab(); + updateDateRangeInLocalDatePickers(DISCOVER_CONTAINER, INITIAL_START_DATE, INITIAL_END_DATE); + waitForDiscoverGridToLoad(); + }); + it('Filter for', () => { + cy.get(GET_DISCOVER_DATA_GRID_CELL(TIMESTAMP_COLUMN_NAME, 0)).then((sub) => { + const selectedTimestamp = sub.text(); + cy.get(GET_DISCOVER_DATA_GRID_CELL(TIMESTAMP_COLUMN_NAME, 0)).realHover(); + cy.get(DISCOVER_CELL_ACTIONS.FILTER_FOR).should('be.visible').trigger('click'); + + cy.get(DISCOVER_FILTER_BADGES).should('have.length', 1); + cy.get(DISCOVER_FILTER_BADGES) + .first() + .should( + 'have.text', + `${TIMESTAMP_COLUMN_NAME}: ${selectedTimestamp} to ${selectedTimestamp}` + ); + }); + }); + it('Filter out', () => { + cy.get(GET_DISCOVER_DATA_GRID_CELL(TIMESTAMP_COLUMN_NAME, 0)).then((sub) => { + const selectedTimestamp = sub.text(); + cy.get(GET_DISCOVER_DATA_GRID_CELL(TIMESTAMP_COLUMN_NAME, 0)).realHover(); + cy.get(DISCOVER_CELL_ACTIONS.FILTER_OUT).should('be.visible').trigger('click'); + cy.get(DISCOVER_FILTER_BADGES).should('have.length', 1); + cy.get(DISCOVER_FILTER_BADGES) + .first() + .should( + 'have.text', + `NOT ${TIMESTAMP_COLUMN_NAME}: ${selectedTimestamp} to ${selectedTimestamp}` + ); + }); + }); + // @TODO: copy is incredibly flaky although it is written same strategy as above tests + // Need to see what is the reaosn for that. Trusting that above tests prove that `Copy` + // will also work correctly. + it.skip('Copy', () => { + grantClipboardReadPerm(); + cy.get(GET_DISCOVER_DATA_GRID_CELL(TIMESTAMP_COLUMN_NAME, 0)).then((sub) => { + const selectedTimestamp = sub.text(); + cy.get(GET_DISCOVER_DATA_GRID_CELL(TIMESTAMP_COLUMN_NAME, 0)).realHover(); + cy.get(DISCOVER_CELL_ACTIONS.EXPAND_CELL_ACTIONS).trigger('click'); + cy.get(DISCOVER_CELL_ACTIONS.EXPANSION_POPOVER).should('be.visible'); + cy.get(DISCOVER_CELL_ACTIONS.COPY).should('be.visible').trigger('click'); + cy.window() + .its('navigator.clipboard') + .then((clipboard) => clipboard.readText()) + .should('eq', selectedTimestamp); + }); + }); + } +); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/discover_state.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/discover_state.cy.ts new file mode 100644 index 0000000000000..510dc37f14f55 --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/discover_state.cy.ts @@ -0,0 +1,93 @@ +/* + * 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 { fillAddFilterForm } from '../../../../tasks/search_bar'; +import { + addDiscoverKqlQuery, + addFieldToTable, + openAddDiscoverFilterPopover, + submitDiscoverSearchBar, + switchDataViewTo, +} from '../../../../tasks/discover'; +import { navigateFromHeaderTo } from '../../../../tasks/security_header'; +import { + DISCOVER_CONTAINER, + DISCOVER_QUERY_INPUT, + DISCOVER_FILTER_BADGES, + DISCOVER_DATA_VIEW_SWITCHER, + GET_DISCOVER_DATA_GRID_CELL_HEADER, +} from '../../../../screens/discover'; +import { updateDateRangeInLocalDatePickers } from '../../../../tasks/date_picker'; +import { login, visit } from '../../../../tasks/login'; +import { + createNewTimeline, + gotToDiscoverTab, + openActiveTimeline, +} from '../../../../tasks/timeline'; +import { ALERTS_URL } from '../../../../urls/navigation'; +import { ALERTS, CSP_FINDINGS } from '../../../../screens/security_header'; + +const INITIAL_START_DATE = 'Jan 18, 2021 @ 20:33:29.186'; +const INITIAL_END_DATE = 'Jan 19, 2024 @ 20:33:29.186'; + +describe( + 'Discover State', + { + env: { ftrConfig: { enableExperimental: ['discoverInTimeline'] } }, + tags: ['@ess', '@serverless'], + }, + () => { + beforeEach(() => { + login(); + visit(ALERTS_URL); + createNewTimeline(); + gotToDiscoverTab(); + updateDateRangeInLocalDatePickers(DISCOVER_CONTAINER, INITIAL_START_DATE, INITIAL_END_DATE); + }); + it('should remember kql query when navigating away and back to discover ', () => { + const kqlQuery = '_id:*'; + addDiscoverKqlQuery(kqlQuery); + submitDiscoverSearchBar(); + navigateFromHeaderTo(CSP_FINDINGS); + navigateFromHeaderTo(ALERTS); + openActiveTimeline(); + gotToDiscoverTab(); + cy.get(DISCOVER_QUERY_INPUT).should('have.text', kqlQuery); + }); + it('should remember filters when navigating away and back to discover ', () => { + openAddDiscoverFilterPopover(); + fillAddFilterForm({ + key: 'agent.type', + value: 'winlogbeat', + }); + navigateFromHeaderTo(CSP_FINDINGS); + navigateFromHeaderTo(ALERTS); + openActiveTimeline(); + gotToDiscoverTab(); + cy.get(DISCOVER_FILTER_BADGES).should('have.length', 1); + }); + it('should remember dataView when navigating away and back to discover ', () => { + const dataviewName = '.kibana-event-log'; + switchDataViewTo(dataviewName); + navigateFromHeaderTo(CSP_FINDINGS); + navigateFromHeaderTo(ALERTS); + openActiveTimeline(); + gotToDiscoverTab(); + cy.get(DISCOVER_DATA_VIEW_SWITCHER.BTN).should('contain.text', dataviewName); + }); + it('should remember columns when navigating away and back to discover ', () => { + addFieldToTable('host.name'); + addFieldToTable('user.name'); + navigateFromHeaderTo(CSP_FINDINGS); + navigateFromHeaderTo(ALERTS); + openActiveTimeline(); + gotToDiscoverTab(); + cy.get(GET_DISCOVER_DATA_GRID_CELL_HEADER('host.name')).should('be.visible'); + cy.get(GET_DISCOVER_DATA_GRID_CELL_HEADER('user.name')).should('be.visible'); + }); + } +); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/search_filter.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/search_filter.cy.ts new file mode 100644 index 0000000000000..5aa4a93247295 --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/search_filter.cy.ts @@ -0,0 +1,139 @@ +/* + * 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 { GET_LOCAL_DATE_PICKER_START_DATE_POPOVER_BUTTON } from '../../../../screens/date_picker'; +import { fillAddFilterForm, fillAddFilterFormAsQueryDSL } from '../../../../tasks/search_bar'; +import { + setStartDate, + updateDateRangeInLocalDatePickers, + updateDates, +} from '../../../../tasks/date_picker'; +import { + DISCOVER_CONTAINER, + DISCOVER_NO_RESULTS, + DISCOVER_RESULT_HITS, + DISCOVER_FILTER_BADGES, + DISCOVER_QUERY_INPUT, + GET_DISCOVER_DATA_GRID_CELL_HEADER, + DISCOVER_DATA_VIEW_SWITCHER, +} from '../../../../screens/discover'; +import { + addDiscoverKqlQuery, + switchDataViewTo, + submitDiscoverSearchBar, + openAddDiscoverFilterPopover, + addFieldToTable, + createAdHocDataView, +} from '../../../../tasks/discover'; +import { createNewTimeline, gotToDiscoverTab } from '../../../../tasks/timeline'; +import { login, visit } from '../../../../tasks/login'; +import { ALERTS_URL } from '../../../../urls/navigation'; + +const INITIAL_START_DATE = 'Jan 18, 2021 @ 20:33:29.186'; +const INITIAL_END_DATE = 'Jan 19, 2024 @ 20:33:29.186'; +const NEW_START_DATE = 'Jan 18, 2023 @ 20:33:29.186'; + +describe( + 'Basic discover search and filter operations', + { + env: { ftrConfig: { enableExperimental: ['discoverInTimeline'] } }, + tags: ['@ess', '@serverless'], + }, + () => { + beforeEach(() => { + login(); + visit(ALERTS_URL); + createNewTimeline(); + gotToDiscoverTab(); + updateDateRangeInLocalDatePickers(DISCOVER_CONTAINER, INITIAL_START_DATE, INITIAL_END_DATE); + }); + it('should change data when dataView is changed', () => { + switchDataViewTo('.kibana-event-log'); + cy.get(DISCOVER_RESULT_HITS).should('have.text', '1'); + }); + + it('should show data according to kql query', () => { + const kqlQuery = '_id:"invalid"'; + addDiscoverKqlQuery(kqlQuery); + submitDiscoverSearchBar(); + cy.get(DISCOVER_NO_RESULTS).should('be.visible'); + }); + it('should show correct data according to filter applied', () => { + openAddDiscoverFilterPopover(); + fillAddFilterForm({ + key: 'agent.type', + value: 'winlogbeat', + }); + cy.get(DISCOVER_FILTER_BADGES).should('have.length', 1); + cy.get(DISCOVER_RESULT_HITS).should('have.text', '1'); + }); + it('should show correct data according to query DSL', () => { + const query = { + bool: { + filter: [ + { + term: { + 'agent.type': 'winlogbeat', + }, + }, + ], + }, + }; + openAddDiscoverFilterPopover(); + fillAddFilterFormAsQueryDSL(JSON.stringify(query)); + cy.get(DISCOVER_FILTER_BADGES).should('have.length', 1); + cy.get(DISCOVER_RESULT_HITS).should('have.text', '1'); + }); + + it('should be able to create ad-hoc dataview without saving', () => { + const adHocDVName = 'adHocDataView'; + const indexPattern = 'audit'; + createAdHocDataView(adHocDVName, indexPattern); + cy.get(DISCOVER_DATA_VIEW_SWITCHER.BTN).should('contain.text', adHocDVName); + }); + + it('should be able to add fields to the table', () => { + addFieldToTable('host.name'); + cy.get(GET_DISCOVER_DATA_GRID_CELL_HEADER('host.name')).should('be.visible'); + addFieldToTable('user.name'); + cy.get(GET_DISCOVER_DATA_GRID_CELL_HEADER('user.name')).should('be.visible'); + }); + + context('navigation', () => { + it('should remove the filter when back is pressed after adding a filter', () => { + openAddDiscoverFilterPopover(); + fillAddFilterForm({ + key: 'agent.type', + value: 'winlogbeat', + }); + cy.get(DISCOVER_FILTER_BADGES).should('have.length', 1); + cy.go('back'); + cy.get(DISCOVER_FILTER_BADGES).should('not.exist'); + }); + it('should removed the query when back is pressed after adding a query', () => { + const kqlQuery = '_id:"invalid"'; + addDiscoverKqlQuery(kqlQuery); + submitDiscoverSearchBar(); + cy.get(DISCOVER_QUERY_INPUT).should('have.text', kqlQuery); + cy.go('back'); + cy.get(DISCOVER_QUERY_INPUT).should('not.have.text', kqlQuery); + }); + + it(`should changed the timerange to ${INITIAL_START_DATE} when back is pressed after modifying timerange from ${INITIAL_START_DATE} to ${NEW_START_DATE} `, () => { + setStartDate(NEW_START_DATE, DISCOVER_CONTAINER); + updateDates(DISCOVER_CONTAINER); + + cy.go('back'); + + cy.get(GET_LOCAL_DATE_PICKER_START_DATE_POPOVER_BUTTON(DISCOVER_CONTAINER)).should( + 'have.text', + INITIAL_START_DATE + ); + }); + }); + } +); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/pagination.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/pagination.cy.ts index a92956be8e134..a8ab36c7bd837 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/pagination.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/pagination.cy.ts @@ -26,7 +26,7 @@ const defaultPageSize = 25; describe('Pagination', { tags: ['@ess', '@serverless'] }, () => { before(() => { cleanKibana(); - cy.task('esArchiverLoad', 'timeline'); + cy.task('esArchiverLoad', { archiveName: 'timeline' }); }); beforeEach(() => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/overview/cti_link_panel.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/overview/cti_link_panel.cy.ts index fd41df836216d..eb6548a5e5834 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/overview/cti_link_panel.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/overview/cti_link_panel.cy.ts @@ -32,7 +32,7 @@ describe('CTI Link Panel', { tags: ['@ess', '@serverless'] }, () => { describe('enabled threat intel module', () => { before(() => { - cy.task('esArchiverLoad', 'threat_indicator'); + cy.task('esArchiverLoad', { archiveName: 'threat_indicator' }); }); beforeEach(() => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/urls/compatibility.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/urls/compatibility.cy.ts index fc08070266dcb..fc58e8d8de698 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/urls/compatibility.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/urls/compatibility.cy.ts @@ -24,7 +24,7 @@ import { import { ABSOLUTE_DATE_RANGE } from '../../urls/state'; import { DATE_PICKER_START_DATE_POPOVER_BUTTON, - DATE_PICKER_END_DATE_POPOVER_BUTTON, + GET_DATE_PICKER_END_DATE_POPOVER_BUTTON, } from '../../screens/date_picker'; const ABSOLUTE_DATE = { @@ -83,6 +83,10 @@ describe('URL compatibility', { tags: ['@ess', '@brokenInServerless'] }, () => { 'title', ABSOLUTE_DATE.startTime ); - cy.get(DATE_PICKER_END_DATE_POPOVER_BUTTON).should('have.attr', 'title', ABSOLUTE_DATE.endTime); + cy.get(GET_DATE_PICKER_END_DATE_POPOVER_BUTTON()).should( + 'have.attr', + 'title', + ABSOLUTE_DATE.endTime + ); }); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/urls/state.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/urls/state.cy.ts index dc7b651084fc8..ca4542310f501 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/urls/state.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/urls/state.cy.ts @@ -7,10 +7,10 @@ import { DATE_PICKER_APPLY_BUTTON_TIMELINE, - DATE_PICKER_END_DATE_POPOVER_BUTTON, - DATE_PICKER_END_DATE_POPOVER_BUTTON_TIMELINE, + GET_DATE_PICKER_END_DATE_POPOVER_BUTTON, + GET_LOCAL_DATE_PICKER_END_DATE_POPOVER_BUTTON, DATE_PICKER_START_DATE_POPOVER_BUTTON, - DATE_PICKER_START_DATE_POPOVER_BUTTON_TIMELINE, + GET_LOCAL_DATE_PICKER_START_DATE_POPOVER_BUTTON, } from '../../screens/date_picker'; import { HOSTS_NAMES } from '../../screens/hosts/all_hosts'; import { ANOMALIES_TAB } from '../../screens/hosts/main'; @@ -23,15 +23,13 @@ import { LOADING_INDICATOR, openNavigationPanel, } from '../../screens/security_header'; -import { TIMELINE_TITLE } from '../../screens/timeline'; +import { TIMELINE_DATE_PICKER_CONTAINER, TIMELINE_TITLE } from '../../screens/timeline'; import { login, visit, visitWithoutDateRange } from '../../tasks/login'; import { + updateDates, setStartDate, setEndDate, - updateDates, - setTimelineStartDate, - setTimelineEndDate, updateTimelineDates, } from '../../tasks/date_picker'; import { openFirstHostDetails, waitForAllHostsToBeLoaded } from '../../tasks/hosts/all_hosts'; @@ -105,7 +103,11 @@ describe('url state', { tags: ['@ess', '@brokenInServerless'] }, () => { 'title', ABSOLUTE_DATE.startTime ); - cy.get(DATE_PICKER_END_DATE_POPOVER_BUTTON).should('have.attr', 'title', ABSOLUTE_DATE.endTime); + cy.get(GET_DATE_PICKER_END_DATE_POPOVER_BUTTON()).should( + 'have.attr', + 'title', + ABSOLUTE_DATE.endTime + ); }); it('sets the url state when start and end date are set', () => { @@ -137,12 +139,12 @@ describe('url state', { tags: ['@ess', '@brokenInServerless'] }, () => { visitWithoutDateRange(ABSOLUTE_DATE_RANGE.url); openTimelineUsingToggle(); - cy.get(DATE_PICKER_START_DATE_POPOVER_BUTTON_TIMELINE).should( + cy.get(GET_LOCAL_DATE_PICKER_START_DATE_POPOVER_BUTTON(TIMELINE_DATE_PICKER_CONTAINER)).should( 'have.attr', 'title', ABSOLUTE_DATE.startTime ); - cy.get(DATE_PICKER_END_DATE_POPOVER_BUTTON_TIMELINE).should( + cy.get(GET_LOCAL_DATE_PICKER_END_DATE_POPOVER_BUTTON(TIMELINE_DATE_PICKER_CONTAINER)).should( 'have.attr', 'title', ABSOLUTE_DATE.endTime @@ -156,16 +158,20 @@ describe('url state', { tags: ['@ess', '@brokenInServerless'] }, () => { 'title', ABSOLUTE_DATE.startTime ); - cy.get(DATE_PICKER_END_DATE_POPOVER_BUTTON).should('have.attr', 'title', ABSOLUTE_DATE.endTime); + cy.get(GET_DATE_PICKER_END_DATE_POPOVER_BUTTON()).should( + 'have.attr', + 'title', + ABSOLUTE_DATE.endTime + ); openTimelineUsingToggle(); - cy.get(DATE_PICKER_START_DATE_POPOVER_BUTTON_TIMELINE).should( + cy.get(GET_LOCAL_DATE_PICKER_START_DATE_POPOVER_BUTTON(TIMELINE_DATE_PICKER_CONTAINER)).should( 'have.attr', 'title', ABSOLUTE_DATE.startTimeTimelineFormatted ); - cy.get(DATE_PICKER_END_DATE_POPOVER_BUTTON_TIMELINE).should( + cy.get(GET_LOCAL_DATE_PICKER_END_DATE_POPOVER_BUTTON(TIMELINE_DATE_PICKER_CONTAINER)).should( 'have.attr', 'title', ABSOLUTE_DATE.endTimeTimelineFormatted @@ -175,9 +181,9 @@ describe('url state', { tags: ['@ess', '@brokenInServerless'] }, () => { it('sets the url state when timeline/global date pickers are unlinked and timeline start and end date are set', () => { visitWithoutDateRange(ABSOLUTE_DATE_RANGE.urlUnlinked); openTimelineUsingToggle(); - setTimelineStartDate(ABSOLUTE_DATE.newStartTimeTyped); + setStartDate(ABSOLUTE_DATE.newStartTimeTyped, TIMELINE_DATE_PICKER_CONTAINER); updateTimelineDates(); - setTimelineEndDate(ABSOLUTE_DATE.newEndTimeTyped); + setEndDate(ABSOLUTE_DATE.newEndTimeTyped, TIMELINE_DATE_PICKER_CONTAINER); updateTimelineDates(); let startDate: string; diff --git a/x-pack/test/security_solution_cypress/cypress/screens/alerts.ts b/x-pack/test/security_solution_cypress/cypress/screens/alerts.ts index f9d464b33faa4..2cd4055dc8dd6 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/alerts.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/alerts.ts @@ -15,7 +15,8 @@ export const ADD_ENDPOINT_EXCEPTION_BTN = '[data-test-subj="add-endpoint-excepti export const ALERT_COUNT_TABLE_COLUMN = (column: number) => `[data-test-subj="embeddablePanel"] [data-test-subj="dataGridRowCell"]:nth-child(${column}) [data-test-subj="lnsTableCellContent"]`; -export const ALERT_EMBEDDABLE_PROGRESS_BAR = '[data-test-subj="embeddablePanel"] .euiProgress'; +export const ALERT_EMBEDDABLE_PROGRESS_BAR = + '[data-test-subj="chartPanels"] [data-test-subj="embeddablePanel"] .euiProgress'; export const ALERT_EMBEDDABLE_EMPTY_PROMPT = '[data-test-subj="embeddablePanel"] [data-test-subj="emptyPlaceholder"]'; diff --git a/x-pack/test/security_solution_cypress/cypress/screens/alerts_details.ts b/x-pack/test/security_solution_cypress/cypress/screens/alerts_details.ts index b4eaaeda35e5d..9d57a2b502f33 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/alerts_details.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/alerts_details.ts @@ -45,7 +45,7 @@ export const OVERVIEW_STATUS = '[data-test-subj="eventDetails"] [data-test-subj= export const EVENT_DETAILS_ALERT_STATUS_POPOVER = '[data-test-subj="event-details-alertStatusPopover"]'; -const SUMMARY_VIEW = '[data-test-subj="summary-view"]'; +export const SUMMARY_VIEW = '[data-test-subj="summary-view"]'; export const TABLE_CELL = '.euiTableRowCell'; diff --git a/x-pack/test/security_solution_cypress/cypress/screens/date_picker.ts b/x-pack/test/security_solution_cypress/cypress/screens/date_picker.ts index 5f5a39b96beb9..1433a60c26a88 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/date_picker.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/date_picker.ts @@ -7,11 +7,13 @@ export const DATE_PICKER_ABSOLUTE_INPUT = '[data-test-subj="superDatePickerAbsoluteDateInput"]'; -export const LOCAL_DATE_PICKER_APPLY_BUTTON = 'button[data-test-subj="querySubmitButton"]'; +export const GET_LOCAL_DATE_PICKER_APPLY_BUTTON = (container: string) => + `${container} button[data-test-subj="querySubmitButton"]`; export const GLOBAL_FILTERS_CONTAINER = `[data-test-subj="filters-global-container"]`; -export const DATE_PICKER_APPLY_BUTTON = `${GLOBAL_FILTERS_CONTAINER} ${LOCAL_DATE_PICKER_APPLY_BUTTON}`; +export const GET_DATE_PICKER_APPLY_BUTTON = (container: string) => + `${container} [data-test-subj="querySubmitButton"]`; export const LOCAL_DATE_PICKER_APPLY_BUTTON_TIMELINE = 'button[data-test-subj="superDatePickerApplyTimeButton"]'; @@ -24,16 +26,15 @@ export const DATE_PICKER_NOW_TAB = '[data-test-subj="superDatePickerNowTab"]'; export const DATE_PICKER_NOW_BUTTON = '[data-test-subj="superDatePickerNowButton"]'; -export const LOCAL_DATE_PICKER_END_DATE_POPOVER_BUTTON = - '[data-test-subj="superDatePickerendDatePopoverButton"]'; +export const GET_LOCAL_DATE_PICKER_END_DATE_POPOVER_BUTTON = (container: string = '') => + `${container} [data-test-subj="superDatePickerendDatePopoverButton"]`; -export const DATE_PICKER_END_DATE_POPOVER_BUTTON = `${GLOBAL_FILTERS_CONTAINER} ${LOCAL_DATE_PICKER_END_DATE_POPOVER_BUTTON}`; +export const GET_DATE_PICKER_END_DATE_POPOVER_BUTTON = ( + container: string = GLOBAL_FILTERS_CONTAINER +) => `${container} [data-test-subj="superDatePickerendDatePopoverButton"]`; export const DATE_PICKER_CONTAINER = `${GLOBAL_FILTERS_CONTAINER} .euiSuperDatePicker`; -export const DATE_PICKER_END_DATE_POPOVER_BUTTON_TIMELINE = - '[data-test-subj="timeline-date-picker-container"] [data-test-subj="superDatePickerendDatePopoverButton"]'; - export const LOCAL_DATE_PICKER_START_DATE_POPOVER_BUTTON = 'button[data-test-subj="superDatePickerstartDatePopoverButton"]'; @@ -41,10 +42,10 @@ export const DATE_PICKER_START_DATE_POPOVER_BUTTON = `${GLOBAL_FILTERS_CONTAINER export const SHOW_DATES_BUTTON = `${GLOBAL_FILTERS_CONTAINER} [data-test-subj="superDatePickerShowDatesButton"]`; -export const GET_LOCAL_SHOW_DATES_BUTTON = (localQueryBarSelector: string) => - `${localQueryBarSelector} [data-test-subj="superDatePickerShowDatesButton"]`; +export const GET_LOCAL_SHOW_DATES_BUTTON = (container: string) => + `${container} [data-test-subj="superDatePickerShowDatesButton"]`; export const DATE_PICKER_SHOW_DATE_POPOVER_BUTTON = `${GLOBAL_FILTERS_CONTAINER} ${SHOW_DATES_BUTTON}`; -export const DATE_PICKER_START_DATE_POPOVER_BUTTON_TIMELINE = - '[data-test-subj="timeline-date-picker-container"] [data-test-subj="superDatePickerstartDatePopoverButton"]'; +export const GET_LOCAL_DATE_PICKER_START_DATE_POPOVER_BUTTON = (container: string = '') => + `${container} [data-test-subj="superDatePickerstartDatePopoverButton"]`; diff --git a/x-pack/test/security_solution_cypress/cypress/screens/discover.ts b/x-pack/test/security_solution_cypress/cypress/screens/discover.ts new file mode 100644 index 0000000000000..577246e5f6568 --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/screens/discover.ts @@ -0,0 +1,79 @@ +/* + * 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 { getDataTestSubjectSelector, getDataTestSubjectSelectorStartWith } from '../helpers/common'; + +export const DISCOVER_CONTAINER_TEST_ID = 'timeline-embedded-discover'; +export const DISCOVER_CONTAINER = getDataTestSubjectSelector(DISCOVER_CONTAINER_TEST_ID); + +export const DISCOVER_DATA_VIEW_SWITCHER = { + BTN: getDataTestSubjectSelector('discover-dataView-switch-link'), + INPUT: getDataTestSubjectSelector('indexPattern-switcher--input'), + GET_DATA_VIEW: (title: string) => `.euiSelectableListItem[role=option][title^="${title}"]`, + CREATE_NEW: getDataTestSubjectSelector('dataview-create-new'), +}; + +export const DISCOVER_DATA_VIEW_EDITOR_FLYOUT = { + MAIN: getDataTestSubjectSelector('indexPatternEditorFlyout'), + NAME_INPUT: getDataTestSubjectSelector('createIndexPatternNameInput'), + INDEX_PATTERN_INPUT: getDataTestSubjectSelector('createIndexPatternTitleInput'), + USE_WITHOUT_SAVING_BTN: getDataTestSubjectSelector('exploreIndexPatternButton'), + SAVE_DATA_VIEW_BTN: getDataTestSubjectSelector('saveIndexPatternButton'), +}; + +export const DISCOVER_QUERY_INPUT = `${DISCOVER_CONTAINER} ${getDataTestSubjectSelector( + 'unifiedQueryInput' +)}`; + +export const DISCOVER_ADD_FILTER = `${DISCOVER_CONTAINER} ${getDataTestSubjectSelector( + 'addFilter' +)}`; + +export const DISCOVER_FILTER_BADGES = `${DISCOVER_CONTAINER} ${getDataTestSubjectSelectorStartWith( + 'filter-badge-' +)}`; + +export const DISCOVER_RESULT_HITS = getDataTestSubjectSelector('unifiedHistogramQueryHits'); + +export const DISCOVER_FIELDS_LOADING = getDataTestSubjectSelector( + 'fieldListGroupedAvailableFields-countLoading' +); + +export const DISCOVER_DATA_GRID_UPDATING = getDataTestSubjectSelector('discoverDataGridUpdating'); + +export const DISCOVER_DATA_GRID_LOADING = getDataTestSubjectSelector('discoverDataGridLoading'); + +export const DISCOVER_NO_RESULTS = getDataTestSubjectSelector('discoverNoResults'); + +export const DISCOVER_TABLE = getDataTestSubjectSelector('docTable'); + +export const GET_DISCOVER_DATA_GRID_CELL = (columnId: string, rowIndex: number) => { + return `${DISCOVER_TABLE} ${getDataTestSubjectSelector( + 'dataGridRowCell' + )}[data-gridcell-column-id="${columnId}"][data-gridcell-row-index="${rowIndex}"] .dscDiscoverGrid__cellValue`; +}; + +export const GET_DISCOVER_DATA_GRID_CELL_HEADER = (columnId: string) => + getDataTestSubjectSelector(`dataGridHeaderCell-${columnId}`); + +export const DISCOVER_CELL_ACTIONS = { + FILTER_FOR: getDataTestSubjectSelector('filterForButton'), + FILTER_OUT: getDataTestSubjectSelector('filterOutButton'), + EXPAND_CELL_ACTIONS: getDataTestSubjectSelector('euiDataGridCellExpandButton'), + EXPANSION_POPOVER: getDataTestSubjectSelector('euiDataGridExpansionPopover'), + COPY: getDataTestSubjectSelector('copyClipboardButton'), +}; + +export const GET_DISCOVER_COLUMN = (columnId: string) => + `${getDataTestSubjectSelector(`dscFieldListPanelField-${columnId}`)}`; + +export const GET_DISCOVER_COLUMN_TOGGLE_BTN = (columnId: string) => + `${getDataTestSubjectSelector(`fieldToggle-${columnId}`)}`; + +export const DISCOVER_FIELD_SEARCH = getDataTestSubjectSelector('fieldListFiltersFieldSearch'); + +export const DISCOVER_FIELD_LIST_LOADING = getDataTestSubjectSelector('fieldListLoading'); diff --git a/x-pack/test/security_solution_cypress/cypress/screens/rule_details.ts b/x-pack/test/security_solution_cypress/cypress/screens/rule_details.ts index 97149957448b6..db13c164b3778 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/rule_details.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/rule_details.ts @@ -29,7 +29,7 @@ export const SAVED_QUERY_DETAILS = /^Saved query$/; export const SAVED_QUERY_FILTERS_DETAILS = 'Saved query filters'; -export const DATA_VIEW_DETAILS = 'Data View'; +export const DATA_VIEW_DETAILS = 'Data view'; export const DEFINITION_DETAILS = '[data-test-subj=definitionRule] [data-test-subj="listItemColumnStepRuleDescription"]'; diff --git a/x-pack/test/security_solution_cypress/cypress/screens/search_bar.ts b/x-pack/test/security_solution_cypress/cypress/screens/search_bar.ts index d90532f8504f3..4517f1e3b2632 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/search_bar.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/search_bar.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { getDataTestSubjectSelector } from '../helpers/common'; + export const GLOBAL_KQL_WRAPPER = '[data-test-subj="filters-global-container"]'; export const GLOBAL_SEARCH_BAR_ADD_FILTER = @@ -46,3 +48,7 @@ export const GLOBAL_KQL_INPUT = `[data-test-subj="filters-global-container"] ${L export const AUTO_SUGGEST_AGENT_NAME = `[data-test-subj="autocompleteSuggestion-field-agent.name-"]`; export const AUTO_SUGGEST_HOST_NAME_VALUE = `[data-test-subj='autocompleteSuggestion-value-"siem-kibana"-']`; + +export const EDIT_AS_QUERY_DSL = getDataTestSubjectSelector('editQueryDSL'); + +export const KIBANA_CODE_EDITOR = getDataTestSubjectSelector('kibanaCodeEditor'); diff --git a/x-pack/test/security_solution_cypress/cypress/screens/timeline.ts b/x-pack/test/security_solution_cypress/cypress/screens/timeline.ts index 6c2cf5fc3ffee..15408d3e57778 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/timeline.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/timeline.ts @@ -6,6 +6,7 @@ */ import type { TimelineFilter } from '../objects/timeline'; +import { getDataTestSubjectSelector } from '../helpers/common'; export const ADD_NOTE_BUTTON = '[data-test-subj="add-note"]'; @@ -339,3 +340,9 @@ export const HOVER_ACTIONS = { export const GET_TIMELINE_HEADER = (fieldName: string) => { return `[data-test-subj="timeline"] [data-test-subj="header-text-${fieldName}"]`; }; + +export const DISCOVER_TAB = getDataTestSubjectSelector('timelineTabs-discover'); + +export const TIMELINE_DATE_PICKER_CONTAINER = getDataTestSubjectSelector( + 'timeline-date-picker-container' +); diff --git a/x-pack/test/security_solution_cypress/cypress/support/es_archiver.ts b/x-pack/test/security_solution_cypress/cypress/support/es_archiver.ts index 42ddb4a526387..bf2277c95e289 100644 --- a/x-pack/test/security_solution_cypress/cypress/support/es_archiver.ts +++ b/x-pack/test/security_solution_cypress/cypress/support/es_archiver.ts @@ -34,7 +34,8 @@ export const esArchiver = ( }); on('task', { - esArchiverLoad: async (archiveName) => esArchiverInstance.load(archiveName), + esArchiverLoad: async ({ archiveName, ...options }) => + esArchiverInstance.load(archiveName, options), esArchiverUnload: async (archiveName) => esArchiverInstance.unload(archiveName), esArchiverResetKibana: async () => esArchiverInstance.emptyKibanaIndex(), }); diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/common/clipboard.ts b/x-pack/test/security_solution_cypress/cypress/tasks/common/clipboard.ts new file mode 100644 index 0000000000000..c6c45c0f32cf7 --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/tasks/common/clipboard.ts @@ -0,0 +1,17 @@ +/* + * 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 grantClipboardReadPerm = () => { + cy.log('Granting Clipboard read permissions'); + Cypress.automation('remote:debugger:protocol', { + command: 'Browser.grantPermissions', + params: { + permissions: ['clipboardReadWrite', 'clipboardSanitizedWrite'], + origin: window.location.origin, + }, + }); +}; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/date_picker.ts b/x-pack/test/security_solution_cypress/cypress/tasks/date_picker.ts index 4a74ac3ddd7fa..1ea45dcd0b91b 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/date_picker.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/date_picker.ts @@ -8,65 +8,32 @@ import { DATE_PICKER_ABSOLUTE_TAB, DATE_PICKER_ABSOLUTE_INPUT, - DATE_PICKER_APPLY_BUTTON, + GET_DATE_PICKER_APPLY_BUTTON, DATE_PICKER_APPLY_BUTTON_TIMELINE, - DATE_PICKER_END_DATE_POPOVER_BUTTON, - DATE_PICKER_END_DATE_POPOVER_BUTTON_TIMELINE, - DATE_PICKER_START_DATE_POPOVER_BUTTON, - SHOW_DATES_BUTTON, - DATE_PICKER_START_DATE_POPOVER_BUTTON_TIMELINE, - DATE_PICKER_SHOW_DATE_POPOVER_BUTTON, + GET_DATE_PICKER_END_DATE_POPOVER_BUTTON, + GET_LOCAL_DATE_PICKER_START_DATE_POPOVER_BUTTON, DATE_PICKER_NOW_TAB, DATE_PICKER_NOW_BUTTON, - LOCAL_DATE_PICKER_APPLY_BUTTON, - LOCAL_DATE_PICKER_END_DATE_POPOVER_BUTTON, - DATE_PICKER_CONTAINER, + GET_LOCAL_DATE_PICKER_APPLY_BUTTON, + GET_LOCAL_DATE_PICKER_END_DATE_POPOVER_BUTTON, GET_LOCAL_SHOW_DATES_BUTTON, + GLOBAL_FILTERS_CONTAINER, } from '../screens/date_picker'; -export const setEndDate = (date: string) => { - cy.get(DATE_PICKER_END_DATE_POPOVER_BUTTON).click({ force: true }); +export const setEndDateNow = (container: string = GLOBAL_FILTERS_CONTAINER) => { + cy.get(GET_DATE_PICKER_END_DATE_POPOVER_BUTTON(container)).click(); - cy.get(DATE_PICKER_ABSOLUTE_TAB).first().click({ force: true }); - - cy.get(DATE_PICKER_ABSOLUTE_INPUT).click(); - cy.get(DATE_PICKER_ABSOLUTE_INPUT).clear(); - cy.get(DATE_PICKER_ABSOLUTE_INPUT).type(date); -}; - -export const setEndDateNow = () => { - cy.get(DATE_PICKER_END_DATE_POPOVER_BUTTON).click({ force: true }); - - cy.get(DATE_PICKER_NOW_TAB).first().click({ force: true }); + cy.get(DATE_PICKER_NOW_TAB).first().click(); cy.get(DATE_PICKER_NOW_BUTTON).click(); }; -export const setStartDate = (date: string) => { - cy.get(DATE_PICKER_CONTAINER).should('be.visible'); - cy.get('body').then(($container) => { - if ($container.find(SHOW_DATES_BUTTON).length > 0) { - cy.get(DATE_PICKER_SHOW_DATE_POPOVER_BUTTON).click({ force: true }); - } else { - cy.get(DATE_PICKER_START_DATE_POPOVER_BUTTON).click({ force: true }); - } - }); +export const setEndDate = (date: string, container: string = GLOBAL_FILTERS_CONTAINER) => { + cy.get(GET_LOCAL_DATE_PICKER_END_DATE_POPOVER_BUTTON(container)).first().click(); - cy.get(DATE_PICKER_ABSOLUTE_TAB).first().click({ force: true }); + cy.get(DATE_PICKER_ABSOLUTE_TAB).first().click(); cy.get(DATE_PICKER_ABSOLUTE_INPUT).click(); - cy.get(DATE_PICKER_ABSOLUTE_INPUT).clear(); - cy.get(DATE_PICKER_ABSOLUTE_INPUT).type(date); - - cy.get(DATE_PICKER_APPLY_BUTTON).click(); -}; - -export const setTimelineEndDate = (date: string) => { - cy.get(DATE_PICKER_END_DATE_POPOVER_BUTTON_TIMELINE).first().click({ force: true }); - - cy.get(DATE_PICKER_ABSOLUTE_TAB).first().click({ force: true }); - - cy.get(DATE_PICKER_ABSOLUTE_INPUT).click({ force: true }); cy.get(DATE_PICKER_ABSOLUTE_INPUT).then(($el) => { if (Cypress.dom.isAttached($el)) { cy.wrap($el).click({ force: true }); @@ -75,10 +42,8 @@ export const setTimelineEndDate = (date: string) => { }); }; -export const setTimelineStartDate = (date: string) => { - cy.get(DATE_PICKER_START_DATE_POPOVER_BUTTON_TIMELINE).first().click({ - force: true, - }); +export const setStartDate = (date: string, container: string = GLOBAL_FILTERS_CONTAINER) => { + cy.get(GET_LOCAL_DATE_PICKER_START_DATE_POPOVER_BUTTON(container)).first().click({}); cy.get(DATE_PICKER_ABSOLUTE_TAB).first().click({ force: true }); @@ -91,9 +56,9 @@ export const setTimelineStartDate = (date: string) => { }); }; -export const updateDates = () => { - cy.get(DATE_PICKER_APPLY_BUTTON).click({ force: true }); - cy.get(DATE_PICKER_APPLY_BUTTON).should('not.have.text', 'Updating'); +export const updateDates = (container: string = GLOBAL_FILTERS_CONTAINER) => { + cy.get(GET_DATE_PICKER_APPLY_BUTTON(container)).click(); + cy.get(GET_DATE_PICKER_APPLY_BUTTON(container)).should('not.have.text', 'Updating'); }; export const updateTimelineDates = () => { @@ -112,15 +77,25 @@ export const updateDateRangeInLocalDatePickers = ( cy.get(DATE_PICKER_ABSOLUTE_INPUT).click(); cy.get(DATE_PICKER_ABSOLUTE_INPUT).clear(); cy.get(DATE_PICKER_ABSOLUTE_INPUT).type(startDate); - cy.get(LOCAL_DATE_PICKER_APPLY_BUTTON).click(); - cy.get(LOCAL_DATE_PICKER_APPLY_BUTTON).should('not.have.text', 'Updating'); + cy.get(GET_LOCAL_DATE_PICKER_APPLY_BUTTON(localQueryBarSelector)).click(); + cy.get(GET_LOCAL_DATE_PICKER_APPLY_BUTTON(localQueryBarSelector)).should( + 'not.have.text', + 'Updating' + ); - cy.get(LOCAL_DATE_PICKER_END_DATE_POPOVER_BUTTON).click(); + cy.get(GET_LOCAL_DATE_PICKER_END_DATE_POPOVER_BUTTON(localQueryBarSelector)).click(); cy.get(DATE_PICKER_ABSOLUTE_TAB).first().click(); cy.get(DATE_PICKER_ABSOLUTE_INPUT).click(); cy.get(DATE_PICKER_ABSOLUTE_INPUT).clear(); cy.get(DATE_PICKER_ABSOLUTE_INPUT).type(endDate); - cy.get(LOCAL_DATE_PICKER_APPLY_BUTTON).click(); + cy.get(GET_LOCAL_DATE_PICKER_APPLY_BUTTON(localQueryBarSelector)).click(); +}; + +export const showStartEndDate = (container: string = GLOBAL_FILTERS_CONTAINER) => { + cy.get(GET_LOCAL_SHOW_DATES_BUTTON(container)).trigger('click'); + cy.get(GET_LOCAL_DATE_PICKER_START_DATE_POPOVER_BUTTON(container)).should('be.visible'); + // close date Popover + cy.get(GET_LOCAL_DATE_PICKER_START_DATE_POPOVER_BUTTON(container)).trigger('click'); }; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/discover.ts b/x-pack/test/security_solution_cypress/cypress/tasks/discover.ts new file mode 100644 index 0000000000000..3da381e1b0af5 --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/tasks/discover.ts @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + DISCOVER_ADD_FILTER, + DISCOVER_CONTAINER, + DISCOVER_DATA_GRID_UPDATING, + DISCOVER_DATA_VIEW_SWITCHER, + DISCOVER_QUERY_INPUT, + GET_DISCOVER_COLUMN_TOGGLE_BTN, + DISCOVER_FIELD_SEARCH, + DISCOVER_DATA_VIEW_EDITOR_FLYOUT, + DISCOVER_FIELD_LIST_LOADING, +} from '../screens/discover'; +import { GET_LOCAL_SEARCH_BAR_SUBMIT_BUTTON } from '../screens/search_bar'; + +export const switchDataViewTo = (dataviewName: string) => { + openDataViewSwitcher(); + cy.get(DISCOVER_DATA_VIEW_SWITCHER.GET_DATA_VIEW(dataviewName)).trigger('click'); + cy.get(DISCOVER_DATA_VIEW_SWITCHER.INPUT).should('not.be.visible'); + cy.get(DISCOVER_DATA_VIEW_SWITCHER.BTN).should('contain.text', dataviewName); +}; + +export const openDataViewSwitcher = () => { + cy.get(DISCOVER_DATA_VIEW_SWITCHER.BTN).click(); + cy.get(DISCOVER_DATA_VIEW_SWITCHER.INPUT).should('be.visible'); +}; + +export const waitForDiscoverGridToLoad = () => { + cy.get(DISCOVER_DATA_GRID_UPDATING).should('be.visible'); + cy.get(DISCOVER_DATA_GRID_UPDATING).should('not.exist'); + cy.get(DISCOVER_FIELD_LIST_LOADING).should('be.visible'); + cy.get(DISCOVER_FIELD_LIST_LOADING).should('not.exist'); +}; + +export const addDiscoverKqlQuery = (kqlQuery: string) => { + cy.get(DISCOVER_QUERY_INPUT).type(kqlQuery); +}; + +export const submitDiscoverSearchBar = () => { + cy.get(GET_LOCAL_SEARCH_BAR_SUBMIT_BUTTON(DISCOVER_CONTAINER)).trigger('click'); +}; + +export const openAddDiscoverFilterPopover = () => { + cy.log(DISCOVER_CONTAINER); + cy.log(GET_LOCAL_SEARCH_BAR_SUBMIT_BUTTON(DISCOVER_CONTAINER)); + cy.get(GET_LOCAL_SEARCH_BAR_SUBMIT_BUTTON(DISCOVER_CONTAINER)).should('be.enabled'); + cy.get(DISCOVER_ADD_FILTER).should('be.visible'); + cy.get(DISCOVER_ADD_FILTER).click(); +}; + +export const searchForField = (fieldId: string) => { + cy.get(DISCOVER_FIELD_SEARCH).type(fieldId); +}; + +export const clearFieldSearch = () => { + cy.get(DISCOVER_FIELD_SEARCH).clear(); +}; + +export const addFieldToTable = (fieldId: string) => { + searchForField(fieldId); + cy.get(GET_DISCOVER_COLUMN_TOGGLE_BTN(fieldId)).first().trigger('click'); + clearFieldSearch(); +}; + +export const createAdHocDataView = (name: string, indexPattern: string, save: boolean = false) => { + openDataViewSwitcher(); + cy.get(DISCOVER_DATA_VIEW_SWITCHER.CREATE_NEW).trigger('click'); + cy.get(DISCOVER_DATA_VIEW_EDITOR_FLYOUT.MAIN).should('be.visible'); + cy.get(DISCOVER_DATA_VIEW_EDITOR_FLYOUT.NAME_INPUT).type(name); + cy.get(DISCOVER_DATA_VIEW_EDITOR_FLYOUT.INDEX_PATTERN_INPUT).type(indexPattern); + if (save) { + cy.get(DISCOVER_DATA_VIEW_EDITOR_FLYOUT.SAVE_DATA_VIEW_BTN).trigger('click'); + } else { + cy.get(DISCOVER_DATA_VIEW_EDITOR_FLYOUT.USE_WITHOUT_SAVING_BTN).trigger('click'); + } + cy.get(DISCOVER_DATA_VIEW_EDITOR_FLYOUT.MAIN).should('not.exist'); +}; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/search_bar.ts b/x-pack/test/security_solution_cypress/cypress/tasks/search_bar.ts index 94d3d064d8e0e..2d33ff2244571 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/search_bar.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/search_bar.ts @@ -19,6 +19,8 @@ import { GLOBAL_KQL_INPUT, LOCAL_KQL_INPUT, GET_LOCAL_SEARCH_BAR_SUBMIT_BUTTON, + EDIT_AS_QUERY_DSL, + KIBANA_CODE_EDITOR, } from '../screens/search_bar'; export const openAddFilterPopover = () => { @@ -75,3 +77,11 @@ export const fillLocalSearchBar = (query: string) => { export const submitLocalSearch = (localSearchBarSelector: string) => { cy.get(GET_LOCAL_SEARCH_BAR_SUBMIT_BUTTON(localSearchBarSelector)).click(); }; + +export const fillAddFilterFormAsQueryDSL = (query: string) => { + cy.get(EDIT_AS_QUERY_DSL).trigger('click'); + cy.get(KIBANA_CODE_EDITOR).type(`{selectAll}{backspace}`); + cy.get(KIBANA_CODE_EDITOR).type(query, { parseSpecialCharSequences: false }); + cy.get(ADD_FILTER_FORM_SAVE_BUTTON).click(); + cy.get(ADD_FILTER_FORM_SAVE_BUTTON).should('not.exist'); +}; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts b/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts index fbec4a495d0fe..ced47a6700e7c 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts @@ -82,6 +82,7 @@ import { TIMELINE_QUERY, PROVIDER_BADGE, PROVIDER_BADGE_DELETE, + DISCOVER_TAB, } from '../screens/timeline'; import { REFRESH_BUTTON, TIMELINE } from '../screens/timelines'; import { drag, drop } from './common'; @@ -135,6 +136,11 @@ export const goToNotesTab = (): Cypress.Chainable> => { return cy.get(NOTES_TAB_BUTTON); }; +export const gotToDiscoverTab = () => { + cy.get(DISCOVER_TAB).click(); + cy.get(DISCOVER_TAB).should('have.class', 'euiTab-isSelected'); +}; + export const goToCorrelationTab = () => { cy.get(TIMELINE_CORRELATION_TAB).click(); cy.get(`${TIMELINE_TAB_CONTENT_EQL} ${TIMELINE_CORRELATION_INPUT}`).should('be.visible'); diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_view_field_editor/field_preview.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_view_field_editor/field_preview.ts new file mode 100644 index 0000000000000..d351c91faea7f --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_view_field_editor/field_preview.ts @@ -0,0 +1,173 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; + +import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; +import { getErrorCodeFromErrorReason } from '@kbn/data-view-field-editor-plugin/public/lib/runtime_field_validation'; +import { + FIELD_PREVIEW_PATH, + INITIAL_REST_VERSION, +} from '@kbn/data-view-field-editor-plugin/common/constants'; +import type { FtrProviderContext } from '../../../ftr_provider_context'; + +const INDEX_NAME = 'api-integration-test-field-preview'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const es = getService('es'); + const svlCommonApi = getService('svlCommonApi'); + + const document = { foo: 1, bar: 'hello' }; + + const createIndex = async () => { + await es.indices.create({ + index: INDEX_NAME, + body: { + mappings: { + properties: { + foo: { + type: 'integer', + }, + bar: { + type: 'keyword', + }, + }, + }, + }, + }); + }; + + const deleteIndex = async () => { + await es.indices.delete({ + index: INDEX_NAME, + }); + }; + + describe('Field preview', function () { + before(async () => await createIndex()); + after(async () => await deleteIndex()); + + describe('should return the script value', () => { + const tests = [ + { + context: 'keyword_field', + script: { + source: 'emit("test")', + }, + expected: 'test', + }, + { + context: 'long_field', + script: { + source: 'emit(doc["foo"].value + 1)', + }, + expected: 2, + }, + { + context: 'keyword_field', + script: { + source: 'emit(doc["bar"].value + " world")', + }, + expected: 'hello world', + }, + ]; + + tests.forEach((test) => { + it(`> ${test.context}`, async () => { + const payload = { + script: test.script, + document, + context: test.context, + index: INDEX_NAME, + }; + + const { body: response } = await supertest + .post(FIELD_PREVIEW_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send(payload) + .set('kbn-xsrf', 'xxx') + .expect(200); + + expect(response.values).eql([test.expected]); + }); + }); + }); + + describe('payload validation', () => { + it('should require a script', async () => { + await supertest + .post(FIELD_PREVIEW_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + context: 'keyword_field', + index: INDEX_NAME, + }) + .set('kbn-xsrf', 'xxx') + .expect(400); + }); + + it('should require a context', async () => { + await supertest + .post(FIELD_PREVIEW_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + script: { source: 'emit("hello")' }, + index: INDEX_NAME, + }) + .set('kbn-xsrf', 'xxx') + .expect(400); + }); + + it('should require an index', async () => { + await supertest + .post(FIELD_PREVIEW_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + script: { source: 'emit("hello")' }, + context: 'keyword_field', + }) + .set('kbn-xsrf', 'xxx') + .expect(400); + }); + }); + + describe('Error messages', () => { + // As ES does not return error codes we will add a test to make sure its error message string + // does not change overtime as we rely on it to extract our own error code. + // If this test fail we'll need to update the "getErrorCodeFromErrorReason()" handler + // TODO: `response.error?.caused_by?.reason` returns + // `class_cast_exception: Cannot cast from [int] to [java.lang.String].` + // in Serverless, which causes `getErrorCodeFromErrorReason` to fail + it.skip('should detect a script casting error', async () => { + const { body: response } = await supertest + .post(FIELD_PREVIEW_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + script: { source: 'emit(123)' }, // We send a long but the type is "keyword" + context: 'keyword_field', + index: INDEX_NAME, + }) + .set('kbn-xsrf', 'xxx'); + + const errorCode = getErrorCodeFromErrorReason(response.error?.caused_by?.reason); + + expect(errorCode).be('CAST_ERROR'); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_view_field_editor/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_view_field_editor/index.ts new file mode 100644 index 0000000000000..561b4798d2c28 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_view_field_editor/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('index pattern field editor', () => { + loadTestFile(require.resolve('./field_preview')); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/constants.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/constants.ts new file mode 100644 index 0000000000000..4292c1da7fa78 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/constants.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + DATA_VIEW_PATH_LEGACY, + SERVICE_KEY_LEGACY, + DATA_VIEW_PATH, + SERVICE_KEY, + SERVICE_PATH, + SERVICE_PATH_LEGACY, +} from '@kbn/data-views-plugin/server'; + +const legacyConfig = { + name: 'legacy index pattern api', + path: DATA_VIEW_PATH_LEGACY, + basePath: SERVICE_PATH_LEGACY, + serviceKey: SERVICE_KEY_LEGACY, +}; + +export const dataViewConfig = { + name: 'data view api', + path: DATA_VIEW_PATH, + basePath: SERVICE_PATH, + serviceKey: SERVICE_KEY, +}; + +export const configArray = [legacyConfig, dataViewConfig]; diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/create_data_view/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/create_data_view/index.ts new file mode 100644 index 0000000000000..b4955e4947bae --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/create_data_view/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('create_index_pattern', () => { + loadTestFile(require.resolve('./validation')); + loadTestFile(require.resolve('./main')); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/create_data_view/main.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/create_data_view/main.ts new file mode 100644 index 0000000000000..c68f5bba43355 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/create_data_view/main.ts @@ -0,0 +1,334 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; +import { configArray } from '../../constants'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const svlCommonApi = getService('svlCommonApi'); + describe('main', () => { + configArray.forEach((config) => { + describe(config.name, () => { + it('can create an index_pattern with just a title', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const response = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + }, + }); + + expect(response.status).to.be(200); + }); + + it('returns back the created index_pattern object', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const response = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + }, + }); + + expect(typeof response.body[config.serviceKey]).to.be('object'); + expect(response.body[config.serviceKey].title).to.be(title); + expect(typeof response.body[config.serviceKey].id).to.be('string'); + expect(response.body[config.serviceKey].id.length > 0).to.be(true); + }); + + it('can specify primitive optional attributes when creating an index pattern', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const id = `test-id-${Date.now()}-${Math.random()}*`; + const response = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + id, + type: 'test-type', + timeFieldName: 'test-timeFieldName', + }, + }); + + expect(response.status).to.be(200); + expect(response.body[config.serviceKey].title).to.be(title); + expect(response.body[config.serviceKey].id).to.be(id); + expect(response.body[config.serviceKey].type).to.be('test-type'); + expect(response.body[config.serviceKey].timeFieldName).to.be('test-timeFieldName'); + }); + + it('can specify optional sourceFilters attribute when creating an index pattern', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const response = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + sourceFilters: [ + { + value: 'foo', + }, + ], + }, + }); + + expect(response.status).to.be(200); + expect(response.body[config.serviceKey].title).to.be(title); + expect(response.body[config.serviceKey].sourceFilters[0].value).to.be('foo'); + }); + + describe('creating fields', () => { + before(async () => { + await esArchiver.load( + 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index' + ); + }); + + after(async () => { + await esArchiver.unload( + 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index' + ); + }); + + it('can specify optional fields attribute when creating an index pattern', async () => { + const title = `basic_index*`; + const response = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + override: true, + [config.serviceKey]: { + title, + fields: { + foo: { + name: 'foo', + // TODO: Scripted fields code dropped since they are not supported in Serverless + customLabel: 'Custom Label', + }, + }, + }, + }); + + expect(response.status).to.be(200); + expect(response.body[config.serviceKey].title).to.be(title); + expect(response.body[config.serviceKey].fields.foo.name).to.be('foo'); + // TODO: Scripted fields code dropped since they are not supported in Serverless + expect(response.body[config.serviceKey].fields.foo.customLabel).to.be('Custom Label'); + + expect(response.body[config.serviceKey].fields.bar.name).to.be('bar'); // created from es index + expect(response.body[config.serviceKey].fields.bar.type).to.be('boolean'); + }); + + // TODO: Scripted fields code dropped since they are not supported in Serverless + it('can add fields created from es index', async () => { + const title = `basic_index*`; + const response = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + override: true, + [config.serviceKey]: { + title, + fields: { + foo: { + name: 'foo', + type: 'string', + }, + fake: { + name: 'fake', + type: 'string', + }, + }, + }, + }); + + expect(response.status).to.be(200); + expect(response.body[config.serviceKey].title).to.be(title); + + expect(response.body[config.serviceKey].fields.foo.name).to.be('foo'); + expect(response.body[config.serviceKey].fields.foo.type).to.be('number'); // picked up from index + + expect(response.body[config.serviceKey].fields.fake).to.be(undefined); // not in index, so not created + }); + + it('can add runtime fields', async () => { + const title = `basic_index*`; + const response = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + override: true, + [config.serviceKey]: { + title, + runtimeFieldMap: { + runtimeFoo: { + type: 'keyword', + script: { + source: 'emit(doc["foo"].value)', + }, + }, + }, + }, + }); + + expect(response.status).to.be(200); + expect(response.body[config.serviceKey].title).to.be(title); + + expect(response.body[config.serviceKey].runtimeFieldMap.runtimeFoo.type).to.be( + 'keyword' + ); + expect(response.body[config.serviceKey].runtimeFieldMap.runtimeFoo.script.source).to.be( + 'emit(doc["foo"].value)' + ); + }); + }); + + it('can specify optional typeMeta attribute when creating an index pattern', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const response = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + typeMeta: {}, + }, + }); + + expect(response.status).to.be(200); + }); + + it('can specify optional fieldFormats attribute when creating an index pattern', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const response = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + fieldFormats: { + foo: { + id: 'test-id', + params: {}, + }, + }, + }, + }); + + expect(response.status).to.be(200); + expect(response.body[config.serviceKey].fieldFormats.foo.id).to.be('test-id'); + expect(response.body[config.serviceKey].fieldFormats.foo.params).to.eql({}); + }); + + it('can specify optional fieldFormats attribute when creating an index pattern', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const response = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + fieldAttrs: { + foo: { + count: 123, + customLabel: 'test', + }, + }, + }, + }); + + expect(response.status).to.be(200); + expect(response.body[config.serviceKey].fieldAttrs.foo.count).to.be(123); + expect(response.body[config.serviceKey].fieldAttrs.foo.customLabel).to.be('test'); + }); + + describe('when creating index pattern with existing name', () => { + it('returns error, by default', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + }, + }); + const response2 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + }, + }); + + expect(response1.status).to.be(200); + expect(response2.status).to.be(400); + }); + + it('succeeds, override flag is set', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + timeFieldName: 'foo', + }, + }); + const response2 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + override: true, + [config.serviceKey]: { + title, + timeFieldName: 'bar', + }, + }); + + expect(response1.status).to.be(200); + expect(response2.status).to.be(200); + + expect(response1.body[config.serviceKey].timeFieldName).to.be('foo'); + expect(response2.body[config.serviceKey].timeFieldName).to.be('bar'); + + expect(response1.body[config.serviceKey].id).to.be( + response1.body[config.serviceKey].id + ); + }); + }); + }); + }); + + // TODO: Removed spaces tests since non-default spaces aren't supported in Serverless + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/create_data_view/validation.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/create_data_view/validation.ts new file mode 100644 index 0000000000000..f4fc964092f65 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/create_data_view/validation.ts @@ -0,0 +1,112 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; +import { configArray } from '../../constants'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const svlCommonApi = getService('svlCommonApi'); + + describe('validation', () => { + configArray.forEach((config) => { + describe(config.name, () => { + it('returns error when index_pattern object is not provided', async () => { + const response = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response.status).to.be(400); + expect(response.body.statusCode).to.be(400); + expect(response.body.message).to.be( + '[request body]: expected a plain object value, but found [null] instead.' + ); + }); + + it('returns error on empty index_pattern object', async () => { + const response = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: {}, + }); + + expect(response.status).to.be(400); + expect(response.body.statusCode).to.be(400); + expect(response.body.message).to.be( + `[request body.${config.serviceKey}.title]: expected value of type [string] but got [undefined]` + ); + }); + + it('returns error when "override" parameter is not a boolean', async () => { + const response = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + override: 123, + [config.serviceKey]: { + title: 'foo', + }, + }); + + expect(response.status).to.be(400); + expect(response.body.statusCode).to.be(400); + expect(response.body.message).to.be( + '[request body.override]: expected value of type [boolean] but got [number]' + ); + }); + + it('returns error when "refresh_fields" parameter is not a boolean', async () => { + const response = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + refresh_fields: 123, + [config.serviceKey]: { + title: 'foo', + }, + }); + + expect(response.status).to.be(400); + expect(response.body.statusCode).to.be(400); + expect(response.body.message).to.be( + '[request body.refresh_fields]: expected value of type [boolean] but got [number]' + ); + }); + + it('returns an error when unknown runtime field type', async () => { + const title = `basic_index*`; + const response = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + override: true, + [config.serviceKey]: { + title, + runtimeFieldMap: { + runtimeFoo: { + type: 'wrong-type', + script: { + source: 'emit(doc["foo"].value)', + }, + }, + }, + }, + }); + + expect(response.status).to.be(400); + }); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/delete_data_view/errors.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/delete_data_view/errors.ts new file mode 100644 index 0000000000000..16681d19c3e45 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/delete_data_view/errors.ts @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; +import { configArray } from '../../constants'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const svlCommonApi = getService('svlCommonApi'); + + describe('errors', () => { + configArray.forEach((config) => { + describe(config.name, () => { + it('returns 404 error on non-existing index_pattern', async () => { + const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`; + const response = await supertest + .delete(`${config.path}/${id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response.status).to.be(404); + }); + + it('returns error when ID is too long', async () => { + const id = `xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx`; + const response = await supertest + .delete(`${config.path}/${id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response.status).to.be(400); + expect(response.body.message).to.be( + '[request params.id]: value has length [1759] but it must have a maximum length of [1000].' + ); + }); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/delete_data_view/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/delete_data_view/index.ts new file mode 100644 index 0000000000000..42eec1aaa1704 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/delete_data_view/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('delete_index_pattern', () => { + loadTestFile(require.resolve('./errors')); + loadTestFile(require.resolve('./main')); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/delete_data_view/main.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/delete_data_view/main.ts new file mode 100644 index 0000000000000..e0fea14780885 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/delete_data_view/main.ts @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; +import { INITIAL_REST_VERSION } from '@kbn/data-views-plugin/server/constants'; +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; +import { configArray } from '../../constants'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const svlCommonApi = getService('svlCommonApi'); + + describe('main', () => { + configArray.forEach((config) => { + describe(config.name, () => { + it('deletes an index_pattern', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const response1 = await supertest + .post(config.path) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + }, + }); + + const response2 = await supertest + .get(`${config.path}/${response1.body[config.serviceKey].id}`) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response2.status).to.be(200); + + const response3 = await supertest + .delete(`${config.path}/${response1.body[config.serviceKey].id}`) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response3.status).to.be(200); + + const response4 = await supertest + .get(`${config.path}/${response1.body[config.serviceKey].id}`) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response4.status).to.be(404); + }); + }); + + it('returns nothing', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const response1 = await supertest + + .post(config.path) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + }, + }); + + await supertest + .get(`${config.path}/${response1.body[config.serviceKey].id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + const response2 = await supertest + .delete(`${config.path}/${response1.body[config.serviceKey].id}`) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + // verify empty response + expect(Object.keys(response2.body).length).to.be(0); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/get_data_view/errors.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/get_data_view/errors.ts new file mode 100644 index 0000000000000..fd69fbaa7e46e --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/get_data_view/errors.ts @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; +import { configArray } from '../../constants'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const svlCommonApi = getService('svlCommonApi'); + + describe('errors', () => { + configArray.forEach((config) => { + describe(config.name, () => { + it('returns 404 error on non-existing index_pattern', async () => { + const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`; + const response = await supertest + .get(`${config.path}/${id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response.status).to.be(404); + }); + + it('returns error when ID is too long', async () => { + const id = `xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx`; + const response = await supertest + .get(`${config.path}/${id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response.status).to.be(400); + expect(response.body.message).to.be( + '[request params.id]: value has length [1759] but it must have a maximum length of [1000].' + ); + }); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/get_data_view/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/get_data_view/index.ts new file mode 100644 index 0000000000000..63cb32bd2f868 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/get_data_view/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('get_index_pattern', () => { + loadTestFile(require.resolve('./errors')); + loadTestFile(require.resolve('./main')); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/get_data_view/main.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/get_data_view/main.ts new file mode 100644 index 0000000000000..d7f3d3b735746 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/get_data_view/main.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; +import { configArray } from '../../constants'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const svlCommonApi = getService('svlCommonApi'); + + describe('main', () => { + configArray.forEach((config) => { + describe(config.name, () => { + it('can retrieve an index_pattern', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + }, + }); + const response2 = await supertest + .get(`${config.path}/${response1.body[config.serviceKey].id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response2.body[config.serviceKey].title).to.be(title); + }); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/get_data_views/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/get_data_views/index.ts new file mode 100644 index 0000000000000..eec88c71b32c6 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/get_data_views/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('get_data_views', () => { + loadTestFile(require.resolve('./main')); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/get_data_views/main.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/get_data_views/main.ts new file mode 100644 index 0000000000000..6b716616de8d7 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/get_data_views/main.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; +import { dataViewConfig } from '../../constants'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const svlCommonApi = getService('svlCommonApi'); + + describe('main', () => { + describe('get data views api', () => { + it('returns list of data views', async () => { + const response = await supertest + .get(dataViewConfig.basePath) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + expect(response.status).to.be(200); + expect(response.body).to.have.property(dataViewConfig.serviceKey); + expect(response.body[dataViewConfig.serviceKey]).to.be.an('array'); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/index.ts new file mode 100644 index 0000000000000..2cf06c123af57 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('index_pattern_crud', () => { + loadTestFile(require.resolve('./create_data_view')); + loadTestFile(require.resolve('./get_data_view')); + loadTestFile(require.resolve('./delete_data_view')); + loadTestFile(require.resolve('./update_data_view')); + loadTestFile(require.resolve('./get_data_views')); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/update_data_view/errors.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/update_data_view/errors.ts new file mode 100644 index 0000000000000..3bbe13af43896 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/update_data_view/errors.ts @@ -0,0 +1,92 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; +import { configArray } from '../../constants'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const svlCommonApi = getService('svlCommonApi'); + + describe('errors', () => { + configArray.forEach((config) => { + describe(config.name, () => { + it('returns error when index_pattern object is not provided', async () => { + const response = await supertest + .post(`${config.path}/foo`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response.status).to.be(400); + expect(response.body.statusCode).to.be(400); + expect(response.body.message).to.be( + '[request body]: expected a plain object value, but found [null] instead.' + ); + }); + + it('returns error on non-existing index_pattern', async () => { + const response = await supertest + .post(`${config.path}/non-existing-index-pattern`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: {}, + }); + + expect(response.status).to.be(404); + expect(response.body.statusCode).to.be(404); + expect(response.body.message).to.be( + 'Saved object [index-pattern/non-existing-index-pattern] not found' + ); + }); + + it('returns error when "refresh_fields" parameter is not a boolean', async () => { + const response = await supertest + .post(`${config.path}/foo`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + refresh_fields: 123, + [config.serviceKey]: { + title: 'foo', + }, + }); + + expect(response.status).to.be(400); + expect(response.body.statusCode).to.be(400); + expect(response.body.message).to.be( + '[request body.refresh_fields]: expected value of type [boolean] but got [number]' + ); + }); + + it('returns success when update patch is empty', async () => { + const title1 = `foo-${Date.now()}-${Math.random()}*`; + const response = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title: title1, + }, + }); + const id = response.body[config.serviceKey].id; + const response2 = await supertest + .post(`${config.path}/${id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: {}, + }); + + expect(response2.status).to.be(200); + }); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/update_data_view/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/update_data_view/index.ts new file mode 100644 index 0000000000000..9e821943a31e3 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/update_data_view/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('update_index_pattern', () => { + loadTestFile(require.resolve('./errors')); + loadTestFile(require.resolve('./main')); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/update_data_view/main.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/update_data_view/main.ts new file mode 100644 index 0000000000000..15e22957a2ca5 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/data_views_crud/update_data_view/main.ts @@ -0,0 +1,445 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; +import { configArray } from '../../constants'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const svlCommonApi = getService('svlCommonApi'); + + describe('main', () => { + configArray.forEach((config) => { + describe(config.name, () => { + it('can update data view title', async () => { + const title1 = `foo-${Date.now()}-${Math.random()}*`; + const title2 = `bar-${Date.now()}-${Math.random()}*`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title: title1, + }, + }); + + expect(response1.body[config.serviceKey].title).to.be(title1); + + const id = response1.body[config.serviceKey].id; + const response2 = await supertest + .post(`${config.path}/${id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title: title2, + }, + }); + + expect(response2.body[config.serviceKey].title).to.be(title2); + + const response3 = await supertest + .get(`${config.path}/${id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response3.body[config.serviceKey].title).to.be(title2); + }); + + it('can update data view name', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const name1 = 'good data view name'; + const name2 = 'better data view name'; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + name: name1, + }, + }); + + expect(response1.body[config.serviceKey].name).to.be(name1); + + const id = response1.body[config.serviceKey].id; + const response2 = await supertest + .post(`${config.path}/${id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + name: name2, + }, + }); + + expect(response2.body[config.serviceKey].name).to.be(name2); + + const response3 = await supertest + .get(`${config.path}/${id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response3.body[config.serviceKey].name).to.be(name2); + }); + + it('can update data view timeFieldName', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + timeFieldName: 'timeFieldName1', + }, + }); + + expect(response1.body[config.serviceKey].timeFieldName).to.be('timeFieldName1'); + + const id = response1.body[config.serviceKey].id; + const response2 = await supertest + .post(`${config.path}/${id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + timeFieldName: 'timeFieldName2', + }, + }); + + expect(response2.body[config.serviceKey].timeFieldName).to.be('timeFieldName2'); + + const response3 = await supertest + .get(`${config.path}/${id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response3.body[config.serviceKey].timeFieldName).to.be('timeFieldName2'); + }); + + it('can update data view sourceFilters', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + sourceFilters: [ + { + value: 'foo', + }, + ], + }, + }); + + expect(response1.body[config.serviceKey].sourceFilters).to.eql([ + { + value: 'foo', + }, + ]); + + const id = response1.body[config.serviceKey].id; + const response2 = await supertest + .post(`${config.path}/${id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + sourceFilters: [ + { + value: 'bar', + }, + { + value: 'baz', + }, + ], + }, + }); + + expect(response2.body[config.serviceKey].sourceFilters).to.eql([ + { + value: 'bar', + }, + { + value: 'baz', + }, + ]); + + const response3 = await supertest + .get(`${config.path}/${id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response3.body[config.serviceKey].sourceFilters).to.eql([ + { + value: 'bar', + }, + { + value: 'baz', + }, + ]); + }); + + it('can update data view fieldFormats', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + fieldFormats: { + foo: { + id: 'foo', + params: { + bar: 'baz', + }, + }, + }, + }, + }); + + expect(response1.body[config.serviceKey].fieldFormats).to.eql({ + foo: { + id: 'foo', + params: { + bar: 'baz', + }, + }, + }); + + const id = response1.body[config.serviceKey].id; + const response2 = await supertest + .post(`${config.path}/${id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + fieldFormats: { + a: { + id: 'a', + params: { + b: 'v', + }, + }, + }, + }, + }); + + expect(response2.body[config.serviceKey].fieldFormats).to.eql({ + a: { + id: 'a', + params: { + b: 'v', + }, + }, + }); + + const response3 = await supertest + .get(`${config.path}/${id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response3.body[config.serviceKey].fieldFormats).to.eql({ + a: { + id: 'a', + params: { + b: 'v', + }, + }, + }); + }); + + it('can update data view type', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + type: 'foo', + }, + }); + + expect(response1.body[config.serviceKey].type).to.be('foo'); + + const id = response1.body[config.serviceKey].id; + const response2 = await supertest + .post(`${config.path}/${id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + type: 'bar', + }, + }); + + expect(response2.body[config.serviceKey].type).to.be('bar'); + + const response3 = await supertest + .get(`${config.path}/${id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response3.body[config.serviceKey].type).to.be('bar'); + }); + + it('can update data view typeMeta', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + typeMeta: { foo: 'bar' }, + }, + }); + + expect(response1.body[config.serviceKey].typeMeta).to.eql({ foo: 'bar' }); + + const id = response1.body[config.serviceKey].id; + const response2 = await supertest + .post(`${config.path}/${id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + typeMeta: { foo: 'baz' }, + }, + }); + + expect(response2.body[config.serviceKey].typeMeta).to.eql({ foo: 'baz' }); + + const response3 = await supertest + .get(`${config.path}/${id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response3.body[config.serviceKey].typeMeta).to.eql({ foo: 'baz' }); + }); + + it('can update multiple data view fields at once', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + timeFieldName: 'timeFieldName1', + typeMeta: { foo: 'bar' }, + }, + }); + + expect(response1.body[config.serviceKey].timeFieldName).to.be('timeFieldName1'); + expect(response1.body[config.serviceKey].typeMeta.foo).to.be('bar'); + + const id = response1.body[config.serviceKey].id; + const response2 = await supertest + .post(`${config.path}/${id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + timeFieldName: 'timeFieldName2', + typeMeta: { baz: 'qux' }, + }, + }); + + expect(response2.body[config.serviceKey].timeFieldName).to.be('timeFieldName2'); + expect(response2.body[config.serviceKey].typeMeta.baz).to.be('qux'); + + const response3 = await supertest + .get(`${config.path}/${id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response3.body[config.serviceKey].timeFieldName).to.be('timeFieldName2'); + expect(response3.body[config.serviceKey].typeMeta.baz).to.be('qux'); + }); + + it('can update runtime fields', async () => { + const title = `basic_index*`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + override: true, + [config.serviceKey]: { + title, + runtimeFieldMap: { + runtimeFoo: { + type: 'keyword', + script: { + source: 'emit(doc["foo"].value)', + }, + }, + }, + }, + }); + + expect(response1.status).to.be(200); + expect(response1.body[config.serviceKey].title).to.be(title); + + expect(response1.body[config.serviceKey].runtimeFieldMap.runtimeFoo.type).to.be( + 'keyword' + ); + expect(response1.body[config.serviceKey].runtimeFieldMap.runtimeFoo.script.source).to.be( + 'emit(doc["foo"].value)' + ); + + const id = response1.body[config.serviceKey].id; + const response2 = await supertest + .post(`${config.path}/${id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + runtimeFieldMap: { + runtimeBar: { + type: 'keyword', + script: { + source: 'emit(doc["foo"].value)', + }, + }, + }, + }, + }); + + expect(response2.body[config.serviceKey].runtimeFieldMap.runtimeBar.type).to.be( + 'keyword' + ); + expect(response2.body[config.serviceKey].runtimeFieldMap.runtimeFoo).to.be(undefined); + + const response3 = await supertest + .get(`${config.path}/${id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response3.body[config.serviceKey].runtimeFieldMap.runtimeBar.type).to.be( + 'keyword' + ); + expect(response3.body[config.serviceKey].runtimeFieldMap.runtimeFoo).to.be(undefined); + }); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/default_index_pattern/default_index_pattern.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/default_index_pattern/default_index_pattern.ts new file mode 100644 index 0000000000000..bdd688f1718aa --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/default_index_pattern/default_index_pattern.ts @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import type { FtrProviderContext } from '../../../../ftr_provider_context'; +import { configArray } from '../constants'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const svlCommonApi = getService('svlCommonApi'); + + describe('default index pattern api', () => { + configArray.forEach((config) => { + describe(config.name, () => { + const newId = () => `default-id-${Date.now()}-${Math.random()}`; + it('can set default index pattern', async () => { + const defaultId = newId(); + const defaultPath = `${config.basePath}/default`; + const serviceKeyId = `${config.serviceKey}_id`; + const response1 = await supertest + .post(defaultPath) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [serviceKeyId]: defaultId, + force: true, + }); + expect(response1.status).to.be(200); + expect(response1.body.acknowledged).to.be(true); + + const response2 = await supertest + .get(defaultPath) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + expect(response2.status).to.be(200); + expect(response2.body[serviceKeyId]).to.be(defaultId); + + const response3 = await supertest + .post(defaultPath) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [serviceKeyId]: newId(), + // no force this time, so this new default shouldn't be set + }); + + expect(response3.status).to.be(200); + const response4 = await supertest + .get(defaultPath) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + expect(response4.body[serviceKeyId]).to.be(defaultId); // original default id is used + + const response5 = await supertest + .post(defaultPath) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [serviceKeyId]: null, + force: true, + }); + expect(response5.status).to.be(200); + + const response6 = await supertest + .get(defaultPath) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + expect(response6.body[serviceKeyId]).to.be(null); + }); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/default_index_pattern/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/default_index_pattern/index.ts new file mode 100644 index 0000000000000..94d73e6141b08 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/default_index_pattern/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('default index pattern', () => { + loadTestFile(require.resolve('./default_index_pattern')); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/es_errors/errors.js b/x-pack/test_serverless/api_integration/test_suites/common/data_views/es_errors/errors.js new file mode 100644 index 0000000000000..ddbc88cce8045 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/es_errors/errors.js @@ -0,0 +1,144 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { errors as esErrors } from '@elastic/elasticsearch'; +import Boom from '@hapi/boom'; + +import { + isEsIndexNotFoundError, + createNoMatchingIndicesError, + isNoMatchingIndicesError, + convertEsError, +} from '@kbn/data-views-plugin/server/fetcher/lib/errors'; + +import { getIndexNotFoundError, getDocNotFoundError } from './lib'; + +export default function ({ getService }) { + const es = getService('es'); + const esArchiver = getService('esArchiver'); + + describe('index_patterns/* error handler', () => { + let indexNotFoundError; + let docNotFoundError; + before(async () => { + await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index'); + indexNotFoundError = await getIndexNotFoundError(es); + docNotFoundError = await getDocNotFoundError(es); + }); + after(async () => { + await esArchiver.unload( + 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index' + ); + }); + + describe('isEsIndexNotFoundError()', () => { + it('identifies index not found errors', () => { + if (!isEsIndexNotFoundError(indexNotFoundError)) { + throw new Error(`Expected isEsIndexNotFoundError(indexNotFoundError) to be true`); + } + }); + + it('rejects doc not found errors', () => { + if (isEsIndexNotFoundError(docNotFoundError)) { + throw new Error(`Expected isEsIndexNotFoundError(docNotFoundError) to be true`); + } + }); + }); + + describe('createNoMatchingIndicesError()', () => { + it('returns a boom error', () => { + const error = createNoMatchingIndicesError(); + if (!error || !error.isBoom) { + throw new Error(`expected ${error} to be a Boom error`); + } + }); + + it('sets output code to "no_matching_indices"', () => { + const error = createNoMatchingIndicesError(); + expect(error.output.payload).to.have.property('code', 'no_matching_indices'); + }); + }); + + describe('isNoMatchingIndicesError()', () => { + it('returns true for errors from createNoMatchingIndicesError()', () => { + if (!isNoMatchingIndicesError(createNoMatchingIndicesError())) { + throw new Error( + 'Expected isNoMatchingIndicesError(createNoMatchingIndicesError()) to be true' + ); + } + }); + + it('returns false for indexNotFoundError', () => { + if (isNoMatchingIndicesError(indexNotFoundError)) { + throw new Error('expected isNoMatchingIndicesError(indexNotFoundError) to be false'); + } + }); + + it('returns false for docNotFoundError', async () => { + if (isNoMatchingIndicesError(docNotFoundError)) { + throw new Error('expected isNoMatchingIndicesError(docNotFoundError) to be false'); + } + }); + }); + + describe('convertEsError()', () => { + const indices = ['foo', 'bar']; + + it('converts indexNotFoundErrors into NoMatchingIndices errors', async () => { + const converted = convertEsError(indices, indexNotFoundError); + if (!isNoMatchingIndicesError(converted)) { + throw new Error( + 'expected convertEsError(indexNotFoundError) to return NoMatchingIndices error' + ); + } + }); + + it('wraps other errors in Boom', async () => { + const error = new esErrors.ResponseError({ + body: { + root_cause: [ + { + type: 'security_exception', + reason: 'action [indices:data/read/field_caps] is unauthorized for user [standard]', + }, + ], + type: 'security_exception', + reason: 'action [indices:data/read/field_caps] is unauthorized for user [standard]', + }, + statusCode: 403, + }); + + expect(error).to.not.have.property('isBoom'); + const converted = convertEsError(indices, error); + expect(converted).to.have.property('isBoom'); + expect(converted.output.statusCode).to.be(403); + }); + + it('handles errors that are already Boom errors', () => { + const error = new Error(); + error.statusCode = 401; + const boomError = Boom.boomify(error, { statusCode: error.statusCode }); + + const converted = convertEsError(indices, boomError); + + expect(converted.output.statusCode).to.be(401); + }); + + it('preserves headers from Boom errors', () => { + const error = new Error(); + error.statusCode = 401; + const boomError = Boom.boomify(error, { statusCode: error.statusCode }); + const wwwAuthenticate = 'Basic realm="Authorization Required"'; + boomError.output.headers['WWW-Authenticate'] = wwwAuthenticate; + const converted = convertEsError(indices, boomError); + + expect(converted.output.headers['WWW-Authenticate']).to.be(wwwAuthenticate); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/es_errors/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/es_errors/index.ts new file mode 100644 index 0000000000000..3fc4c06262100 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/es_errors/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('index_patterns/service/lib', () => { + loadTestFile(require.resolve('./errors')); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/es_errors/lib/get_es_errors.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/es_errors/lib/get_es_errors.ts new file mode 100644 index 0000000000000..9821cbd73c142 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/es_errors/lib/get_es_errors.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { Client } from '@elastic/elasticsearch'; + +export async function getIndexNotFoundError(es: Client) { + try { + await es.indices.get({ + index: 'SHOULD NOT EXIST', + }); + } catch (err) { + expect(err).to.have.property('statusCode', 404); // basic check + return err; + } + + throw new Error('Expected es.indices.get() call to fail'); +} + +export async function getDocNotFoundError(es: Client) { + try { + await es.get({ + index: 'basic_index', + id: '1234', + }); + } catch (err) { + expect(err).to.have.property('statusCode', 404); // basic check + return err; + } + + throw new Error('Expected es.get() call to fail'); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/es_errors/lib/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/es_errors/lib/index.ts new file mode 100644 index 0000000000000..b188b824512ef --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/es_errors/lib/index.ts @@ -0,0 +1,8 @@ +/* + * 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 { getIndexNotFoundError, getDocNotFoundError } from './get_es_errors'; diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/fields_api/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/fields_api/index.ts new file mode 100644 index 0000000000000..0fcf5454e7eba --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/fields_api/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('fields_api', () => { + loadTestFile(require.resolve('./update_fields')); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/fields_api/update_fields/errors.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/fields_api/update_fields/errors.ts new file mode 100644 index 0000000000000..2e8e8556b17e9 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/fields_api/update_fields/errors.ts @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; +import { configArray } from '../../constants'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const svlCommonApi = getService('svlCommonApi'); + + describe('errors', () => { + configArray.forEach((config) => { + describe(config.name, () => { + it('returns 404 error on non-existing index_pattern', async () => { + const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`; + const response = await supertest + .post(`${config.path}/${id}/fields`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + fields: { + foo: {}, + }, + }); + + expect(response.status).to.be(404); + }); + + it('returns error when "fields" payload attribute is invalid', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + }, + }); + const response2 = await supertest + .post(`${config.path}/${response1.body[config.serviceKey].id}/fields`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + fields: 123, + }); + + expect(response2.status).to.be(400); + expect(response2.body.statusCode).to.be(400); + expect(response2.body.message).to.be( + '[request body.fields]: expected value of type [object] but got [number]' + ); + }); + + it('returns error if not changes are specified', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + }, + }); + + const response2 = await supertest + .post(`${config.path}/${response1.body[config.serviceKey].id}/fields`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + fields: { + foo: {}, + bar: {}, + baz: {}, + }, + }); + + expect(response2.status).to.be(400); + expect(response2.body.statusCode).to.be(400); + expect(response2.body.message).to.be('Change set is empty.'); + }); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/fields_api/update_fields/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/fields_api/update_fields/index.ts new file mode 100644 index 0000000000000..a716524e8ba22 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/fields_api/update_fields/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('update_fields', () => { + loadTestFile(require.resolve('./errors')); + loadTestFile(require.resolve('./main')); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/fields_api/update_fields/main.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/fields_api/update_fields/main.ts new file mode 100644 index 0000000000000..2e72648900747 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/fields_api/update_fields/main.ts @@ -0,0 +1,523 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; +import { configArray } from '../../constants'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const svlCommonApi = getService('svlCommonApi'); + + describe('main', () => { + const basicIndex = 'ba*ic_index'; + let indexPattern: any; + + configArray.forEach((config) => { + describe(config.name, () => { + before(async () => { + await esArchiver.load( + 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index' + ); + + indexPattern = ( + await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + override: true, + [config.serviceKey]: { + title: basicIndex, + }, + }) + ).body[config.serviceKey]; + }); + + after(async () => { + await esArchiver.unload( + 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index' + ); + + if (indexPattern) { + await supertest + .delete(`${config.path}/${indexPattern.id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + } + }); + + it('can update multiple fields', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + }, + }); + + expect(response1.status).to.be(200); + expect(response1.body[config.serviceKey].fieldAttrs.foo).to.be(undefined); + expect(response1.body[config.serviceKey].fieldAttrs.bar).to.be(undefined); + + const response2 = await supertest + .post(`${config.path}/${response1.body[config.serviceKey].id}/fields`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + fields: { + foo: { + count: 123, + customLabel: 'test', + }, + bar: { + count: 456, + }, + }, + }); + + expect(response2.status).to.be(200); + expect(response2.body[config.serviceKey].fieldAttrs.foo.count).to.be(123); + expect(response2.body[config.serviceKey].fieldAttrs.foo.customLabel).to.be('test'); + expect(response2.body[config.serviceKey].fieldAttrs.bar.count).to.be(456); + + const response3 = await supertest + .get(`${config.path}/${response1.body[config.serviceKey].id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response3.status).to.be(200); + expect(response3.body[config.serviceKey].fieldAttrs.foo.count).to.be(123); + expect(response3.body[config.serviceKey].fieldAttrs.foo.customLabel).to.be('test'); + expect(response3.body[config.serviceKey].fieldAttrs.bar.count).to.be(456); + }); + + describe('count', () => { + it('can set field "count" attribute on non-existing field', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + }, + }); + + expect(response1.status).to.be(200); + expect(response1.body[config.serviceKey].fieldAttrs.foo).to.be(undefined); + + const response2 = await supertest + .post(`${config.path}/${response1.body[config.serviceKey].id}/fields`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + fields: { + foo: { + count: 123, + }, + }, + }); + + expect(response2.status).to.be(200); + expect(response2.body[config.serviceKey].fieldAttrs.foo.count).to.be(123); + + const response3 = await supertest + .get(`${config.path}/${response1.body[config.serviceKey].id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response3.status).to.be(200); + expect(response3.body[config.serviceKey].fieldAttrs.foo.count).to.be(123); + }); + + it('can update "count" attribute in index_pattern attribute map', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + fieldAttrs: { + foo: { + count: 1, + }, + }, + }, + }); + + expect(response1.status).to.be(200); + expect(response1.body[config.serviceKey].fieldAttrs.foo.count).to.be(1); + + const response2 = await supertest + .post(`${config.path}/${response1.body[config.serviceKey].id}/fields`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + fields: { + foo: { + count: 2, + }, + }, + }); + + expect(response2.status).to.be(200); + expect(response2.body[config.serviceKey].fieldAttrs.foo.count).to.be(2); + + const response3 = await supertest + .get(`${config.path}/${response1.body[config.serviceKey].id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response3.status).to.be(200); + expect(response3.body[config.serviceKey].fieldAttrs.foo.count).to.be(2); + }); + + it('can delete "count" attribute from index_pattern attribute map', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + fieldAttrs: { + foo: { + count: 1, + }, + }, + }, + }); + + expect(response1.status).to.be(200); + expect(response1.body[config.serviceKey].fieldAttrs.foo.count).to.be(1); + + const response2 = await supertest + .post(`${config.path}/${response1.body[config.serviceKey].id}/fields`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + fields: { + foo: { + count: null, + }, + }, + }); + + expect(response2.status).to.be(200); + expect(response2.body[config.serviceKey].fieldAttrs.foo.count).to.be(undefined); + + const response3 = await supertest + .get(`${config.path}/${response1.body[config.serviceKey].id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response3.status).to.be(200); + expect(response3.body[config.serviceKey].fieldAttrs.foo.count).to.be(undefined); + }); + }); + + describe('customLabel', () => { + it('can set field "customLabel" attribute on non-existing field', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + }, + }); + + expect(response1.status).to.be(200); + expect(response1.body[config.serviceKey].fieldAttrs.foo).to.be(undefined); + + const response2 = await supertest + .post(`${config.path}/${response1.body[config.serviceKey].id}/fields`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + fields: { + foo: { + customLabel: 'foo', + }, + }, + }); + + expect(response2.status).to.be(200); + expect(response2.body[config.serviceKey].fieldAttrs.foo.customLabel).to.be('foo'); + + const response3 = await supertest + .get(`${config.path}/${response1.body[config.serviceKey].id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response3.status).to.be(200); + expect(response3.body[config.serviceKey].fieldAttrs.foo.customLabel).to.be('foo'); + }); + + it('can update "customLabel" attribute in index_pattern attribute map', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + fieldAttrs: { + foo: { + customLabel: 'foo', + }, + }, + }, + }); + + expect(response1.status).to.be(200); + expect(response1.body[config.serviceKey].fieldAttrs.foo.customLabel).to.be('foo'); + + const response2 = await supertest + .post(`${config.path}/${response1.body[config.serviceKey].id}/fields`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + fields: { + foo: { + customLabel: 'bar', + }, + }, + }); + + expect(response2.status).to.be(200); + expect(response2.body[config.serviceKey].fieldAttrs.foo.customLabel).to.be('bar'); + + const response3 = await supertest + .get(`${config.path}/${response1.body[config.serviceKey].id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response3.status).to.be(200); + expect(response3.body[config.serviceKey].fieldAttrs.foo.customLabel).to.be('bar'); + }); + + it('can delete "customLabel" attribute from index_pattern attribute map', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + fieldAttrs: { + foo: { + customLabel: 'foo', + }, + }, + }, + }); + + expect(response1.status).to.be(200); + expect(response1.body[config.serviceKey].fieldAttrs.foo.customLabel).to.be('foo'); + + const response2 = await supertest + .post(`${config.path}/${response1.body[config.serviceKey].id}/fields`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + fields: { + foo: { + customLabel: null, + }, + }, + }); + + expect(response2.status).to.be(200); + expect(response2.body[config.serviceKey].fieldAttrs.foo.customLabel).to.be(undefined); + + const response3 = await supertest + .get(`${config.path}/${response1.body[config.serviceKey].id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response3.status).to.be(200); + expect(response3.body[config.serviceKey].fieldAttrs.foo.customLabel).to.be(undefined); + }); + + it('can set field "customLabel" attribute on an existing field', async () => { + await supertest + .post(`${config.path}/${indexPattern.id}/fields`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + fields: { + foo: { + customLabel: 'baz', + }, + }, + }); + + const response1 = await supertest + .get(`${config.path}/${indexPattern.id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response1.status).to.be(200); + expect(response1.body[config.serviceKey].fields.foo.customLabel).to.be('baz'); + }); + }); + + describe('format', () => { + it('can set field "format" attribute on non-existing field', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + }, + }); + + expect(response1.status).to.be(200); + expect(response1.body[config.serviceKey].fieldFormats.foo).to.be(undefined); + + const response2 = await supertest + .post(`${config.path}/${response1.body[config.serviceKey].id}/fields`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + fields: { + foo: { + format: { + id: 'bar', + params: { baz: 'qux' }, + }, + }, + }, + }); + + expect(response2.status).to.be(200); + expect(response2.body[config.serviceKey].fieldFormats.foo).to.eql({ + id: 'bar', + params: { baz: 'qux' }, + }); + + const response3 = await supertest + .get(`${config.path}/${response1.body[config.serviceKey].id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response3.status).to.be(200); + expect(response3.body[config.serviceKey].fieldFormats.foo).to.eql({ + id: 'bar', + params: { baz: 'qux' }, + }); + }); + + it('can update "format" attribute in index_pattern format map', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + fieldFormats: { + foo: { + id: 'bar', + params: { + baz: 'qux', + }, + }, + }, + }, + }); + + expect(response1.status).to.be(200); + expect(response1.body[config.serviceKey].fieldFormats.foo).to.eql({ + id: 'bar', + params: { + baz: 'qux', + }, + }); + + const response2 = await supertest + .post(`${config.path}/${response1.body[config.serviceKey].id}/fields`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + fields: { + foo: { + format: { + id: 'bar-2', + params: { baz: 'qux-2' }, + }, + }, + }, + }); + + expect(response2.status).to.be(200); + expect(response2.body[config.serviceKey].fieldFormats.foo).to.eql({ + id: 'bar-2', + params: { baz: 'qux-2' }, + }); + + const response3 = await supertest + .get(`${config.path}/${response1.body[config.serviceKey].id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response3.status).to.be(200); + expect(response3.body[config.serviceKey].fieldFormats.foo).to.eql({ + id: 'bar-2', + params: { baz: 'qux-2' }, + }); + }); + + it('can remove "format" attribute from index_pattern format map', async () => { + const response2 = await supertest + .post(`${config.path}/${indexPattern.id}/fields`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + fields: { + foo: { + format: null, + }, + }, + }); + + expect(response2.status).to.be(200); + expect(response2.body[config.serviceKey].fieldFormats.foo).to.be(undefined); + + const response3 = await supertest + .get(`${config.path}/${indexPattern.id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response3.status).to.be(200); + expect(response3.body[config.serviceKey].fieldFormats.foo).to.be(undefined); + }); + + // TODO: Scripted fields code dropped since they are not supported in Serverless + }); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/fields_for_wildcard_route/conflicts.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/fields_for_wildcard_route/conflicts.ts new file mode 100644 index 0000000000000..d1d08c1e88832 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/fields_for_wildcard_route/conflicts.ts @@ -0,0 +1,83 @@ +/* + * 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 { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; +import { INITIAL_REST_VERSION_INTERNAL } from '@kbn/data-views-plugin/server/constants'; +import { FIELDS_FOR_WILDCARD_PATH } from '@kbn/data-views-plugin/common/constants'; +import expect from '@kbn/expect'; +import type { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const svlCommonApi = getService('svlCommonApi'); + + describe('conflicts', () => { + before(() => + esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/conflicts') + ); + after(() => + esArchiver.unload('test/api_integration/fixtures/es_archiver/index_patterns/conflicts') + ); + + it('flags fields with mismatched types as conflicting', () => + supertest + .get(FIELDS_FOR_WILDCARD_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .query({ pattern: 'logs-*' }) + .expect(200) + .then((resp) => { + expect(resp.body).to.eql({ + fields: [ + { + name: '@timestamp', + type: 'date', + esTypes: ['date'], + aggregatable: true, + searchable: true, + readFromDocValues: true, + metadata_field: false, + }, + { + name: 'number_conflict', + type: 'number', + esTypes: ['float', 'integer'], + aggregatable: true, + searchable: true, + readFromDocValues: true, + metadata_field: false, + }, + { + name: 'string_conflict', + type: 'string', + esTypes: ['keyword', 'text'], + aggregatable: true, + searchable: true, + readFromDocValues: true, + metadata_field: false, + }, + { + name: 'success', + type: 'conflict', + esTypes: ['keyword', 'boolean'], + aggregatable: true, + searchable: true, + readFromDocValues: false, + conflictDescriptions: { + boolean: ['logs-2017.01.02'], + keyword: ['logs-2017.01.01'], + }, + metadata_field: false, + }, + ], + indices: ['logs-2017.01.01', 'logs-2017.01.02'], + }); + })); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/fields_for_wildcard_route/filter.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/fields_for_wildcard_route/filter.ts new file mode 100644 index 0000000000000..ff2ad5e5d00a1 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/fields_for_wildcard_route/filter.ts @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; +import { INITIAL_REST_VERSION_INTERNAL } from '@kbn/data-views-plugin/server/constants'; +import { FIELDS_FOR_WILDCARD_PATH } from '@kbn/data-views-plugin/common/constants'; +import expect from '@kbn/expect'; +import type { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const es = getService('es'); + const svlCommonApi = getService('svlCommonApi'); + + describe('filter fields', () => { + before(async () => { + await es.index({ + index: 'helloworld1', + refresh: true, + id: 'helloworld', + body: { hello: 'world' }, + }); + + await es.index({ + index: 'helloworld2', + refresh: true, + id: 'helloworld2', + body: { bye: 'world' }, + }); + }); + + it('can filter', async () => { + const a = await supertest + .put(FIELDS_FOR_WILDCARD_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .query({ pattern: 'helloworld*' }) + .send({ index_filter: { exists: { field: 'bye' } } }); + + const fieldNames = a.body.fields.map((fld: { name: string }) => fld.name); + + expect(fieldNames.indexOf('bye') > -1).to.be(true); + expect(fieldNames.indexOf('hello') === -1).to.be(true); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/fields_for_wildcard_route/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/fields_for_wildcard_route/index.ts new file mode 100644 index 0000000000000..a64ddadf2f7a2 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/fields_for_wildcard_route/index.ts @@ -0,0 +1,17 @@ +/* + * 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 type { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('index_patterns/_fields_for_wildcard route', () => { + loadTestFile(require.resolve('./params')); + loadTestFile(require.resolve('./conflicts')); + loadTestFile(require.resolve('./response')); + loadTestFile(require.resolve('./filter')); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/fields_for_wildcard_route/params.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/fields_for_wildcard_route/params.ts new file mode 100644 index 0000000000000..035ca1af4b5a8 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/fields_for_wildcard_route/params.ts @@ -0,0 +1,172 @@ +/* + * 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 { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; +import { INITIAL_REST_VERSION_INTERNAL } from '@kbn/data-views-plugin/server/constants'; +import { FIELDS_FOR_WILDCARD_PATH } from '@kbn/data-views-plugin/common/constants'; +import type { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertest'); + const randomness = getService('randomness'); + const svlCommonApi = getService('svlCommonApi'); + + describe('params', () => { + before(() => + esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index') + ); + after(() => + esArchiver.unload('test/api_integration/fixtures/es_archiver/index_patterns/basic_index') + ); + + it('requires a pattern query param', () => + supertest + .get(FIELDS_FOR_WILDCARD_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .query({}) + .expect(400)); + + it('accepts include_unmapped param', () => + supertest + .get(FIELDS_FOR_WILDCARD_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .query({ + pattern: '*', + include_unmapped: true, + }) + .expect(200)); + + it('rejects unexpected query params', () => + supertest + .get(FIELDS_FOR_WILDCARD_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .query({ + pattern: randomness.word(), + [randomness.word()]: randomness.word(), + }) + .expect(400)); + + describe('fields', () => { + it('accepts a JSON formatted fields query param', () => + supertest + .get(FIELDS_FOR_WILDCARD_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .query({ + pattern: '*', + fields: JSON.stringify(['baz']), + }) + .expect(200)); + + it('accepts meta_fields query param in string array', () => + supertest + .get(FIELDS_FOR_WILDCARD_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .query({ + pattern: '*', + fields: ['baz', 'foo'], + }) + .expect(200)); + + it('accepts single array fields query param', () => + supertest + .get(FIELDS_FOR_WILDCARD_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .query({ + pattern: '*', + fields: ['baz'], + }) + .expect(200)); + + it('accepts single fields query param', () => + supertest + .get(FIELDS_FOR_WILDCARD_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .query({ + pattern: '*', + fields: 'baz', + }) + .expect(200)); + + it('rejects a comma-separated list of fields', () => + supertest + .get(FIELDS_FOR_WILDCARD_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .query({ + pattern: '*', + fields: 'foo,bar', + }) + .expect(400)); + }); + + describe('meta_fields', () => { + it('accepts a JSON formatted meta_fields query param', () => + supertest + .get(FIELDS_FOR_WILDCARD_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .query({ + pattern: '*', + meta_fields: JSON.stringify(['meta']), + }) + .expect(200)); + + it('accepts meta_fields query param in string array', () => + supertest + .get(FIELDS_FOR_WILDCARD_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .query({ + pattern: '*', + meta_fields: ['_id', 'meta'], + }) + .expect(200)); + + it('accepts single meta_fields query param', () => + supertest + .get(FIELDS_FOR_WILDCARD_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .query({ + pattern: '*', + meta_fields: ['_id'], + }) + .expect(200)); + + it('rejects a comma-separated list of meta_fields', () => + supertest + .get(FIELDS_FOR_WILDCARD_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .query({ + pattern: '*', + meta_fields: 'foo,bar', + }) + .expect(400)); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/fields_for_wildcard_route/response.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/fields_for_wildcard_route/response.ts new file mode 100644 index 0000000000000..a073dac456b7f --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/fields_for_wildcard_route/response.ts @@ -0,0 +1,243 @@ +/* + * 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 { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; +import { INITIAL_REST_VERSION_INTERNAL } from '@kbn/data-views-plugin/server/constants'; +import { FIELDS_FOR_WILDCARD_PATH } from '@kbn/data-views-plugin/common/constants'; +import expect from '@kbn/expect'; +import { sortBy } from 'lodash'; +import type { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertest'); + const svlCommonApi = getService('svlCommonApi'); + + const ensureFieldsAreSorted = (resp: { body: { fields: { name: string } } }) => { + expect(resp.body.fields).to.eql(sortBy(resp.body.fields, 'name')); + }; + + const testFields = [ + { + type: 'boolean', + esTypes: ['boolean'], + searchable: true, + aggregatable: true, + name: 'bar', + readFromDocValues: true, + metadata_field: false, + }, + { + type: 'string', + esTypes: ['text'], + searchable: true, + aggregatable: false, + name: 'baz', + readFromDocValues: false, + metadata_field: false, + }, + { + type: 'string', + esTypes: ['keyword'], + searchable: true, + aggregatable: true, + name: 'baz.keyword', + readFromDocValues: true, + subType: { multi: { parent: 'baz' } }, + metadata_field: false, + }, + { + type: 'number', + esTypes: ['long'], + searchable: true, + aggregatable: true, + name: 'foo', + readFromDocValues: true, + metadata_field: false, + }, + { + aggregatable: true, + esTypes: ['keyword'], + name: 'nestedField.child', + readFromDocValues: true, + searchable: true, + subType: { + nested: { + path: 'nestedField', + }, + }, + type: 'string', + metadata_field: false, + }, + ]; + + describe('fields_for_wildcard_route response', () => { + before(() => + esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index') + ); + after(() => + esArchiver.unload('test/api_integration/fixtures/es_archiver/index_patterns/basic_index') + ); + + it('returns a flattened version of the fields in es', async () => { + await supertest + .get(FIELDS_FOR_WILDCARD_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .query({ pattern: 'basic_index' }) + .expect(200, { + fields: testFields, + indices: ['basic_index'], + }) + .then(ensureFieldsAreSorted); + }); + + it('returns a single field as requested', async () => { + await supertest + .get(FIELDS_FOR_WILDCARD_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .query({ pattern: 'basic_index', fields: JSON.stringify(['bar']) }) + .expect(200, { + fields: [testFields[0]], + indices: ['basic_index'], + }); + }); + + it('always returns a field for all passed meta fields', async () => { + await supertest + .get(FIELDS_FOR_WILDCARD_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .query({ + pattern: 'basic_index', + meta_fields: JSON.stringify(['_id', '_source', 'crazy_meta_field']), + }) + .expect(200, { + fields: [ + { + aggregatable: false, + name: '_id', + esTypes: ['_id'], + readFromDocValues: false, + searchable: true, + type: 'string', + metadata_field: true, + }, + { + aggregatable: false, + name: '_source', + esTypes: ['_source'], + readFromDocValues: false, + searchable: false, + type: '_source', + metadata_field: true, + }, + { + type: 'boolean', + esTypes: ['boolean'], + searchable: true, + aggregatable: true, + name: 'bar', + readFromDocValues: true, + metadata_field: false, + }, + { + aggregatable: false, + name: 'baz', + esTypes: ['text'], + readFromDocValues: false, + searchable: true, + type: 'string', + metadata_field: false, + }, + { + type: 'string', + esTypes: ['keyword'], + searchable: true, + aggregatable: true, + name: 'baz.keyword', + readFromDocValues: true, + subType: { multi: { parent: 'baz' } }, + metadata_field: false, + }, + { + aggregatable: false, + name: 'crazy_meta_field', + readFromDocValues: false, + searchable: false, + type: 'string', + metadata_field: true, + }, + { + type: 'number', + esTypes: ['long'], + searchable: true, + aggregatable: true, + name: 'foo', + readFromDocValues: true, + metadata_field: false, + }, + { + aggregatable: true, + esTypes: ['keyword'], + name: 'nestedField.child', + readFromDocValues: true, + searchable: true, + subType: { + nested: { + path: 'nestedField', + }, + }, + type: 'string', + metadata_field: false, + }, + ], + indices: ['basic_index'], + }) + .then(ensureFieldsAreSorted); + }); + + it('returns fields when one pattern exists and the other does not', async () => { + await supertest + .get(FIELDS_FOR_WILDCARD_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .query({ pattern: 'bad_index,basic_index' }) + .expect(200, { + fields: testFields, + indices: ['basic_index'], + }); + }); + + it('returns 404 when neither exists', async () => { + await supertest + .get(FIELDS_FOR_WILDCARD_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .query({ pattern: 'bad_index,bad_index_2' }) + .expect(404); + }); + + it('returns 404 when no patterns exist', async () => { + await supertest + .get(FIELDS_FOR_WILDCARD_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .query({ + pattern: 'bad_index', + }) + .expect(404); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/has_user_index_pattern/has_user_index_pattern.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/has_user_index_pattern/has_user_index_pattern.ts new file mode 100644 index 0000000000000..19f4d82d79a8e --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/has_user_index_pattern/has_user_index_pattern.ts @@ -0,0 +1,112 @@ +/* + * 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 { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; +import { + INITIAL_REST_VERSION, + INITIAL_REST_VERSION_INTERNAL, +} from '@kbn/data-views-plugin/server/constants'; +import expect from '@kbn/expect'; +import type { FtrProviderContext } from '../../../../ftr_provider_context'; +import { configArray } from '../constants'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const es = getService('es'); + const svlCommonApi = getService('svlCommonApi'); + const kibanaServer = getService('kibanaServer'); + + describe('has user index pattern API', () => { + configArray.forEach((config) => { + describe(config.name, () => { + beforeEach(async () => { + // TODO: emptyKibanaIndex fails in Serverless with + // "index_not_found_exception: no such index [.kibana_ingest]", + // so it was switched to `savedObjects.cleanStandardList()` + await kibanaServer.savedObjects.cleanStandardList(); + if (await es.indices.exists({ index: 'metrics-test' })) { + await es.indices.delete({ index: 'metrics-test' }); + } + + if (await es.indices.exists({ index: 'logs-test' })) { + await es.indices.delete({ index: 'logs-test' }); + } + }); + + const servicePath = `${config.basePath}/has_user_${config.serviceKey}`; + + it('should return false if no index patterns', async () => { + // Make sure all saved objects including data views are cleared + // TODO: emptyKibanaIndex fails in Serverless with + // "index_not_found_exception: no such index [.kibana_ingest]", + // so it was switched to `savedObjects.cleanStandardList()` + await kibanaServer.savedObjects.cleanStandardList(); + const response = await supertest + .get(servicePath) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + expect(response.status).to.be(200); + expect(response.body.result).to.be(false); + }); + + it('should return true if has index pattern with user data', async () => { + await esArchiver.load( + 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index' + ); + await supertest + .post(config.path) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + override: true, + [config.serviceKey]: { + title: 'basic_index', + }, + }); + + const response = await supertest + .get(servicePath) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + expect(response.status).to.be(200); + expect(response.body.result).to.be(true); + + await esArchiver.unload( + 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index' + ); + }); + + it('should return true if has user index pattern without data', async () => { + await supertest + .post(config.path) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + override: true, + [config.serviceKey]: { + title: 'basic_index', + allowNoIndex: true, + }, + }); + + const response = await supertest + .get(servicePath) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + expect(response.status).to.be(200); + expect(response.body.result).to.be(true); + }); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/has_user_index_pattern/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/has_user_index_pattern/index.ts new file mode 100644 index 0000000000000..4ec3661feffde --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/has_user_index_pattern/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('has user index pattern', () => { + loadTestFile(require.resolve('./has_user_index_pattern')); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/index.ts new file mode 100644 index 0000000000000..eb25b2530a5e7 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('index_patterns', () => { + loadTestFile(require.resolve('./es_errors')); + loadTestFile(require.resolve('./fields_for_wildcard_route')); + loadTestFile(require.resolve('./data_views_crud')); + // TODO: Removed `scripted_fields_crud` since + // scripted fields are not supported in Serverless + loadTestFile(require.resolve('./fields_api')); + loadTestFile(require.resolve('./default_index_pattern')); + loadTestFile(require.resolve('./runtime_fields_crud')); + loadTestFile(require.resolve('./integration')); + // TODO: Removed `deprecations` since + // scripted fields are not supported in Serverless + loadTestFile(require.resolve('./has_user_index_pattern')); + loadTestFile(require.resolve('./swap_references')); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/integration/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/integration/index.ts new file mode 100644 index 0000000000000..55a9585ee7f1f --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/integration/index.ts @@ -0,0 +1,17 @@ +/* + * 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 type { FtrProviderContext } from '../../../../ftr_provider_context'; + +/** + * Test usage of different index patterns APIs in combination + */ +export default function ({ loadTestFile }: FtrProviderContext) { + describe('integration', () => { + loadTestFile(require.resolve('./integration')); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/integration/integration.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/integration/integration.ts new file mode 100644 index 0000000000000..8b9c215754f12 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/integration/integration.ts @@ -0,0 +1,134 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import _ from 'lodash'; +import type { FtrProviderContext } from '../../../../ftr_provider_context'; + +/** + * Test usage of different index patterns APIs in combination + */ +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const svlCommonApi = getService('svlCommonApi'); + + describe('integration', () => { + before(async () => { + await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index'); + }); + + after(async () => { + await esArchiver.unload( + 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index' + ); + }); + + it('create an index pattern, add a runtime field, add a field formatter, then re-create the same index pattern', async () => { + const title = `basic_index*`; + const response1 = await supertest + .post('/api/index_patterns/index_pattern') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + override: true, + index_pattern: { + title, + }, + }); + const id = response1.body.index_pattern.id; + const response2 = await supertest + .post(`/api/index_patterns/index_pattern/${id}/runtime_field`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + name: 'runtimeBar', + runtimeField: { + type: 'long', + script: { + source: "emit(doc['field_name'].value)", + }, + }, + }); + + expect(response2.status).to.be(200); + + const response3 = await supertest + .post(`/api/index_patterns/index_pattern/${response1.body.index_pattern.id}/fields`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + fields: { + runtimeBar: { + count: 123, + customLabel: 'test', + }, + }, + }); + + expect(response3.status).to.be(200); + + const response4 = await supertest + .post(`/api/index_patterns/index_pattern/${response1.body.index_pattern.id}/fields`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + fields: { + runtimeBar: { + format: { + id: 'duration', + params: { inputFormat: 'milliseconds', outputFormat: 'humanizePrecise' }, + }, + }, + }, + }); + + expect(response4.status).to.be(200); + + const response5 = await supertest + .get('/api/index_patterns/index_pattern/' + response1.body.index_pattern.id) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response5.status).to.be(200); + + const resultIndexPattern = response5.body.index_pattern; + + const runtimeField = resultIndexPattern.fields.runtimeBar; + expect(runtimeField.name).to.be('runtimeBar'); + expect(runtimeField.runtimeField.type).to.be('long'); + expect(runtimeField.runtimeField.script.source).to.be("emit(doc['field_name'].value)"); + expect(runtimeField.scripted).to.be(false); + + expect(resultIndexPattern.fieldFormats.runtimeBar.id).to.be('duration'); + expect(resultIndexPattern.fieldFormats.runtimeBar.params.inputFormat).to.be('milliseconds'); + expect(resultIndexPattern.fieldFormats.runtimeBar.params.outputFormat).to.be( + 'humanizePrecise' + ); + + expect(resultIndexPattern.fieldAttrs.runtimeBar.count).to.be(123); + expect(resultIndexPattern.fieldAttrs.runtimeBar.customLabel).to.be('test'); + + // check that retrieved object is transient and a clone can be created + const response6 = await supertest + .post('/api/index_patterns/index_pattern') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + override: true, + index_pattern: resultIndexPattern, + }); + + expect(response6.status).to.be(200); + const recreatedIndexPattern = response6.body.index_pattern; + + expect(_.omit(recreatedIndexPattern, 'version', 'namespaces')).to.eql( + _.omit(resultIndexPattern, 'version', 'namespaces') + ); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/create_runtime_field/errors.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/create_runtime_field/errors.ts new file mode 100644 index 0000000000000..6fa86503eb75a --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/create_runtime_field/errors.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; +import { configArray } from '../../constants'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const svlCommonApi = getService('svlCommonApi'); + + describe('errors', () => { + configArray.forEach((config) => { + describe(config.name, () => { + it('returns an error field object is not provided', async () => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title, + }, + }); + const id = response1.body[config.serviceKey].id; + const response2 = await supertest + .post(`${config.path}/${id}/runtime_field`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({}); + + expect(response2.status).to.be(400); + expect(response2.body.statusCode).to.be(400); + expect(response2.body.message).to.be( + '[request body.name]: expected value of type [string] but got [undefined]' + ); + }); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/create_runtime_field/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/create_runtime_field/index.ts new file mode 100644 index 0000000000000..226a2026ef945 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/create_runtime_field/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('create_runtime_field', () => { + loadTestFile(require.resolve('./errors')); + loadTestFile(require.resolve('./main')); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/create_runtime_field/main.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/create_runtime_field/main.ts new file mode 100644 index 0000000000000..9b01977f2fdc5 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/create_runtime_field/main.ts @@ -0,0 +1,229 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; +import { configArray } from '../../constants'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const svlCommonApi = getService('svlCommonApi'); + + describe('main', () => { + before(async () => { + await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index'); + }); + + after(async () => { + await esArchiver.unload( + 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index' + ); + }); + + configArray.forEach((config) => { + describe(config.name, () => { + it('can create a new runtime field', async () => { + const title = `basic_index*`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + override: true, + [config.serviceKey]: { + title, + }, + }); + const id = response1.body[config.serviceKey].id; + const response2 = await supertest + .post(`${config.path}/${id}/runtime_field`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + name: 'runtimeBar', + runtimeField: { + type: 'long', + script: { + source: "emit(doc['field_name'].value)", + }, + }, + }); + + expect(response2.status).to.be(200); + expect(response2.body[config.serviceKey]).to.not.be.empty(); + + const field = + config.serviceKey === 'index_pattern' ? response2.body.field : response2.body.fields[0]; + + expect(field.name).to.be('runtimeBar'); + expect(field.runtimeField.type).to.be('long'); + expect(field.runtimeField.script.source).to.be("emit(doc['field_name'].value)"); + expect(field.scripted).to.be(false); + }); + + it('newly created runtime field is available in the index_pattern object', async () => { + const title = `basic_index`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + override: true, + [config.serviceKey]: { + title, + }, + }); + + await supertest + .post(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + name: 'runtimeBar', + runtimeField: { + type: 'long', + script: { + source: "emit(doc['field_name'].value)", + }, + }, + }); + + const response2 = await supertest + .get(`${config.path}/${response1.body[config.serviceKey].id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response2.status).to.be(200); + expect(response2.body[config.serviceKey]).to.not.be.empty(); + + const field = response2.body[config.serviceKey].fields.runtimeBar; + + expect(field.name).to.be('runtimeBar'); + expect(field.runtimeField.type).to.be('long'); + expect(field.runtimeField.script.source).to.be("emit(doc['field_name'].value)"); + expect(field.scripted).to.be(false); + + const response3 = await supertest + .post(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + name: 'runtimeBar', + runtimeField: { + type: 'long', + script: { + source: "emit(doc['field_name'].value)", + }, + }, + }); + + expect(response3.status).to.be(400); + + await supertest + .delete(`${config.path}/${response1.body[config.serviceKey].id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + }); + + it('prevents field name collisions', async () => { + const title = `basic*`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + override: true, + [config.serviceKey]: { + title, + }, + }); + + const response2 = await supertest + .post(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + name: 'runtimeBar', + runtimeField: { + type: 'long', + script: { + source: "emit(doc['field_name'].value)", + }, + }, + }); + + expect(response2.status).to.be(200); + + const response3 = await supertest + .post(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + name: 'runtimeBar', + runtimeField: { + type: 'long', + script: { + source: "emit(doc['field_name'].value)", + }, + }, + }); + + expect(response3.status).to.be(400); + + const response4 = await supertest + .post(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + name: 'runtimeComposite', + runtimeField: { + type: 'composite', + script: { + source: 'emit("a","a"); emit("b","b")', + }, + fields: { + a: { + type: 'keyword', + }, + b: { + type: 'keyword', + }, + }, + }, + }); + + expect(response4.status).to.be(200); + + const response5 = await supertest + .post(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + name: 'runtimeComposite', + runtimeField: { + type: 'composite', + script: { + source: 'emit("a","a"); emit("b","b")', + }, + fields: { + a: { + type: 'keyword', + }, + b: { + type: 'keyword', + }, + }, + }, + }); + + expect(response5.status).to.be(400); + }); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/delete_runtime_field/errors.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/delete_runtime_field/errors.ts new file mode 100644 index 0000000000000..6cdd3c1e042d4 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/delete_runtime_field/errors.ts @@ -0,0 +1,88 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; +import { configArray } from '../../constants'; + +export default function ({ getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertest'); + const svlCommonApi = getService('svlCommonApi'); + + describe('errors', () => { + const basicIndex = 'b*sic_index'; + let indexPattern: any; + + configArray.forEach((config) => { + describe(config.name, () => { + before(async () => { + await esArchiver.load( + 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index' + ); + + indexPattern = ( + await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title: basicIndex, + }, + }) + ).body[config.serviceKey]; + }); + + after(async () => { + await esArchiver.unload( + 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index' + ); + + if (indexPattern) { + await supertest + .delete(`${config.path}/${indexPattern.id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + } + }); + + it('returns 404 error on non-existing index_pattern', async () => { + const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`; + const response = await supertest + .delete(`${config.path}/${id}/runtime_field/foo`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response.status).to.be(404); + }); + + it('returns 404 error on non-existing runtime field', async () => { + const response1 = await supertest + .delete(`${config.path}/${indexPattern.id}/runtime_field/test`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response1.status).to.be(404); + }); + + it('returns error when ID is too long', async () => { + const id = `xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx`; + const response = await supertest + .delete(`${config.path}/${id}/runtime_field/foo`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response.status).to.be(400); + expect(response.body.message).to.be( + '[request params.id]: value has length [1759] but it must have a maximum length of [1000].' + ); + }); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/delete_runtime_field/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/delete_runtime_field/index.ts new file mode 100644 index 0000000000000..434c986417696 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/delete_runtime_field/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('delete_runtime_field', () => { + loadTestFile(require.resolve('./errors')); + loadTestFile(require.resolve('./main')); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/delete_runtime_field/main.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/delete_runtime_field/main.ts new file mode 100644 index 0000000000000..a93268adde3b5 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/delete_runtime_field/main.ts @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; +import { configArray } from '../../constants'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const svlCommonApi = getService('svlCommonApi'); + + describe('main', () => { + before(async () => { + await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index'); + }); + + after(async () => { + await esArchiver.unload( + 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index' + ); + }); + + configArray.forEach((config) => { + describe(config.name, () => { + it('can delete a runtime field', async () => { + const title = `basic_index*`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + override: true, + [config.serviceKey]: { + title, + runtimeFieldMap: { + runtimeBar: { + type: 'long', + script: { + source: "emit(doc['field_name'].value)", + }, + }, + }, + }, + }); + + const response2 = await supertest + .get(`${config.path}/${response1.body[config.serviceKey].id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(typeof response2.body[config.serviceKey].fields.runtimeBar).to.be('object'); + + const response3 = await supertest + .delete( + `${config.path}/${response1.body[config.serviceKey].id}/runtime_field/runtimeBar` + ) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response3.status).to.be(200); + + const response4 = await supertest + .get(`${config.path}/${response1.body[config.serviceKey].id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(typeof response4.body[config.serviceKey].fields.runtimeBar).to.be('undefined'); + await supertest + .delete(`${config.path}/${response1.body[config.serviceKey].id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + }); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/get_runtime_field/errors.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/get_runtime_field/errors.ts new file mode 100644 index 0000000000000..19d329fe0f410 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/get_runtime_field/errors.ts @@ -0,0 +1,88 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; +import { configArray } from '../../constants'; + +export default function ({ getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertest'); + const svlCommonApi = getService('svlCommonApi'); + + describe('errors', () => { + const basicIndex = '*asic_index'; + let indexPattern: any; + + configArray.forEach((config) => { + describe(config.name, () => { + before(async () => { + await esArchiver.load( + 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index' + ); + + indexPattern = ( + await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + [config.serviceKey]: { + title: basicIndex, + }, + }) + ).body[config.serviceKey]; + }); + + after(async () => { + await esArchiver.unload( + 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index' + ); + + if (indexPattern) { + await supertest + .delete(`${config.path}/${indexPattern.id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + } + }); + + it('returns 404 error on non-existing index_pattern', async () => { + const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`; + const response = await supertest + .get(`${config.path}/${id}/runtime_field/foo`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response.status).to.be(404); + }); + + it('returns 404 error on non-existing runtime field', async () => { + const response1 = await supertest + .get(`${config.path}/${indexPattern.id}/runtime_field/sf`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response1.status).to.be(404); + }); + + it('returns error when ID is too long', async () => { + const id = `xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx`; + const response = await supertest + .get(`${config.path}/${id}/runtime_field/foo`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response.status).to.be(400); + expect(response.body.message).to.be( + '[request params.id]: value has length [1759] but it must have a maximum length of [1000].' + ); + }); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/get_runtime_field/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/get_runtime_field/index.ts new file mode 100644 index 0000000000000..7f22ba407e651 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/get_runtime_field/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('get_runtime_field', () => { + loadTestFile(require.resolve('./errors')); + loadTestFile(require.resolve('./main')); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/get_runtime_field/main.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/get_runtime_field/main.ts new file mode 100644 index 0000000000000..638c4f99bd509 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/get_runtime_field/main.ts @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; +import { configArray } from '../../constants'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const svlCommonApi = getService('svlCommonApi'); + + describe('main', () => { + before(async () => { + await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index'); + }); + + after(async () => { + await esArchiver.unload( + 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index' + ); + }); + + configArray.forEach((config) => { + describe(config.name, () => { + it('can fetch a runtime field', async () => { + const title = `basic_index*`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + override: true, + [config.serviceKey]: { + title, + runtimeFieldMap: { + runtimeFoo: { + type: 'keyword', + script: { + source: "emit(doc['field_name'].value)", + }, + }, + runtimeBar: { + type: 'keyword', + script: { + source: "emit(doc['field_name'].value)", + }, + }, + }, + }, + }); + + expect(response1.status).to.be(200); + + const response2 = await supertest + .get(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field/runtimeFoo`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + const field = + config.serviceKey === 'index_pattern' ? response2.body.field : response2.body.fields[0]; + + expect(response2.status).to.be(200); + expect(response2.body[config.serviceKey]).to.not.be.empty(); + expect(typeof field).to.be('object'); + expect(field.name).to.be('runtimeFoo'); + expect(field.type).to.be('string'); + expect(field.scripted).to.be(false); + expect(field.runtimeField.script.source).to.be("emit(doc['field_name'].value)"); + await supertest + .delete(`${config.path}/${response1.body[config.serviceKey].id}`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + }); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/index.ts new file mode 100644 index 0000000000000..5b95e3b39dcf4 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('runtime_fields_crud', () => { + loadTestFile(require.resolve('./create_runtime_field')); + loadTestFile(require.resolve('./get_runtime_field')); + loadTestFile(require.resolve('./delete_runtime_field')); + loadTestFile(require.resolve('./put_runtime_field')); + loadTestFile(require.resolve('./update_runtime_field')); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/put_runtime_field/errors.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/put_runtime_field/errors.ts new file mode 100644 index 0000000000000..1141ca5db1ec0 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/put_runtime_field/errors.ts @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; +import { configArray } from '../../constants'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const svlCommonApi = getService('svlCommonApi'); + + describe('errors', () => { + before(async () => { + await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index'); + }); + + after(async () => { + await esArchiver.unload( + 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index' + ); + }); + + configArray.forEach((config) => { + describe(config.name, () => { + it('returns 404 error on non-existing index_pattern', async () => { + const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`; + const response = await supertest + .put(`${config.path}/${id}/runtime_field`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + name: 'runtimeBar', + runtimeField: { + type: 'long', + script: { + source: "emit(doc['field_name'].value)", + }, + }, + }); + + expect(response.status).to.be(404); + }); + + it('returns error on non-runtime field update attempt', async () => { + const title = `basic_index`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + override: true, + [config.serviceKey]: { + title, + }, + }); + + const response2 = await supertest + .put(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + name: 'bar', + runtimeField: { + type: 'long', + script: { + source: "emit(doc['field_name'].value)", + }, + }, + }); + + expect(response2.status).to.be(400); + expect(response2.body.message).to.be('Only runtime fields can be updated'); + }); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/put_runtime_field/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/put_runtime_field/index.ts new file mode 100644 index 0000000000000..85f3c93570a06 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/put_runtime_field/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('put_runtime_field', () => { + loadTestFile(require.resolve('./errors')); + loadTestFile(require.resolve('./main')); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/put_runtime_field/main.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/put_runtime_field/main.ts new file mode 100644 index 0000000000000..01cf160208898 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/put_runtime_field/main.ts @@ -0,0 +1,147 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; +import { configArray } from '../../constants'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const svlCommonApi = getService('svlCommonApi'); + + describe('main', () => { + before(async () => { + await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index'); + }); + + after(async () => { + await esArchiver.unload( + 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index' + ); + }); + + configArray.forEach((config) => { + describe(config.name, () => { + it('can overwrite an existing field', async () => { + const title = `basic_index`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + override: true, + [config.serviceKey]: { + title, + runtimeFieldMap: { + runtimeFoo: { + type: 'keyword', + script: { + source: "doc['field_name'].value", + }, + }, + runtimeBar: { + type: 'keyword', + script: { + source: "doc['field_name'].value", + }, + }, + }, + }, + }); + + const response2 = await supertest + .put(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + name: 'runtimeFoo', + runtimeField: { + type: 'long', + script: { + source: "doc['field_name'].value", + }, + }, + }); + + expect(response2.status).to.be(200); + expect(response2.body[config.serviceKey]).to.not.be.empty(); + + const response3 = await supertest + .get(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field/runtimeFoo`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + const field3 = + config.serviceKey === 'index_pattern' ? response3.body.field : response3.body.fields[0]; + + expect(response3.status).to.be(200); + expect(field3.type).to.be('number'); + + const response4 = await supertest + .get(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field/runtimeBar`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + const field4 = + config.serviceKey === 'index_pattern' ? response4.body.field : response4.body.fields[0]; + + expect(response4.status).to.be(200); + expect(field4.type).to.be('string'); + }); + + it('can add a new runtime field', async () => { + const title = `basic_index`; + const response1 = await supertest + .post(config.path) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + override: true, + [config.serviceKey]: { + title, + runtimeFieldMap: { + runtimeFoo: { + type: 'keyword', + script: { + source: "doc['field_name'].value", + }, + }, + }, + }, + }); + + await supertest + .put(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + name: 'runtimeBar', + runtimeField: { + type: 'long', + script: { + source: "doc['field_name'].value", + }, + }, + }); + + const response2 = await supertest + .get(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field/runtimeBar`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + const field = + config.serviceKey === 'index_pattern' ? response2.body.field : response2.body.fields[0]; + + expect(response2.status).to.be(200); + expect(response2.body[config.serviceKey]).to.not.be.empty(); + expect(typeof field.runtimeField).to.be('object'); + }); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/update_runtime_field/errors.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/update_runtime_field/errors.ts new file mode 100644 index 0000000000000..17e4711fdb9aa --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/update_runtime_field/errors.ts @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; +import { configArray } from '../../constants'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const svlCommonApi = getService('svlCommonApi'); + + describe('errors', () => { + configArray.forEach((config) => { + describe(config.name, () => { + it('returns 404 error on non-existing index_pattern', async () => { + const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`; + const response = await supertest + .post(`${config.path}/${id}/runtime_field/foo`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + runtimeField: { + type: 'keyword', + script: { + source: "doc['something_new'].value", + }, + }, + }); + + expect(response.status).to.be(404); + }); + + it('returns error when field name is specified', async () => { + const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`; + const response = await supertest + .post(`${config.path}/${id}/runtime_field/foo`) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + name: 'foo', + runtimeField: { + type: 'keyword', + script: { + source: "doc['something_new'].value", + }, + }, + }); + + expect(response.status).to.be(400); + expect(response.body.statusCode).to.be(400); + expect(response.body.message).to.be( + "[request body.name]: a value wasn't expected to be present" + ); + }); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/update_runtime_field/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/update_runtime_field/index.ts new file mode 100644 index 0000000000000..d60fcf7f047a1 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/update_runtime_field/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('update_runtime_field', () => { + loadTestFile(require.resolve('./errors')); + loadTestFile(require.resolve('./main')); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/update_runtime_field/main.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/update_runtime_field/main.ts new file mode 100644 index 0000000000000..385ae4f548440 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/runtime_fields_crud/update_runtime_field/main.ts @@ -0,0 +1,114 @@ +/* + * 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 { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; +import { INITIAL_REST_VERSION } from '@kbn/data-views-plugin/server/constants'; +import expect from '@kbn/expect'; +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; +import { configArray } from '../../constants'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const svlCommonApi = getService('svlCommonApi'); + + describe('main', () => { + before(async () => { + await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index'); + }); + + after(async () => { + await esArchiver.unload( + 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index' + ); + }); + + configArray.forEach((config) => { + describe(config.name, () => { + it('can update an existing field', async () => { + const title = `basic_index`; + const response1 = await supertest + .post(config.path) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + override: true, + [config.serviceKey]: { + title, + runtimeFieldMap: { + runtimeFoo: { + type: 'keyword', + script: { + source: "doc['field_name'].value", + }, + }, + runtimeBar: { + type: 'keyword', + script: { + source: "doc['field_name'].value", + }, + }, + }, + }, + }); + + const response2 = await supertest + .post(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field/runtimeFoo`) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + runtimeField: { + type: 'keyword', + script: { + source: "doc['something_new'].value", + }, + }, + }); + + expect(response2.status).to.be(200); + + const response3 = await supertest + .get(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field/runtimeFoo`) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + const field = + config.serviceKey === 'index_pattern' ? response3.body.field : response3.body.fields[0]; + + expect(response3.status).to.be(200); + expect(response3.body[config.serviceKey]).to.not.be.empty(); + expect(field.type).to.be('string'); + expect(field.runtimeField.type).to.be('keyword'); + expect(field.runtimeField.script.source).to.be("doc['something_new'].value"); + + // Partial update + const response4 = await supertest + .post(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field/runtimeFoo`) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + runtimeField: { + script: { + source: "doc['partial_update'].value", + }, + }, + }); + + expect(response4.status).to.be(200); + const field2 = + config.serviceKey === 'index_pattern' ? response4.body.field : response4.body.fields[0]; + + expect(field2.runtimeField.script.source).to.be("doc['partial_update'].value"); + }); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/swap_references/errors.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/swap_references/errors.ts new file mode 100644 index 0000000000000..0762f52bd0c44 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/swap_references/errors.ts @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; +import { INITIAL_REST_VERSION } from '@kbn/data-views-plugin/server/constants'; +import type { FtrProviderContext } from '../../../../ftr_provider_context'; +import { dataViewConfig } from '../constants'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const svlCommonApi = getService('svlCommonApi'); + + describe('errors', () => { + it('returns 404 error on non-existing index_pattern', async () => { + const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`; + const response = await supertest + .get(`${dataViewConfig.path}/${id}`) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response.status).to.be(404); + }); + + it('returns error when ID is too long', async () => { + const id = `xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx`; + const response = await supertest + .get(`${dataViewConfig.path}/${id}`) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(response.status).to.be(400); + expect(response.body.message).to.be( + '[request params.id]: value has length [1759] but it must have a maximum length of [1000].' + ); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/swap_references/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/swap_references/index.ts new file mode 100644 index 0000000000000..fc3c15d0d473a --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/swap_references/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('swap_references', () => { + loadTestFile(require.resolve('./errors')); + loadTestFile(require.resolve('./main')); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/data_views/swap_references/main.ts b/x-pack/test_serverless/api_integration/test_suites/common/data_views/swap_references/main.ts new file mode 100644 index 0000000000000..a2184a294e67a --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/data_views/swap_references/main.ts @@ -0,0 +1,212 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { + DATA_VIEW_SWAP_REFERENCES_PATH, + SPECIFIC_DATA_VIEW_PATH, + DATA_VIEW_PATH, +} from '@kbn/data-views-plugin/server'; +import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; +import { INITIAL_REST_VERSION } from '@kbn/data-views-plugin/server/constants'; +import type { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const svlCommonApi = getService('svlCommonApi'); + const title = 'logs-*'; + const prevDataViewId = '91200a00-9efd-11e7-acb3-3dab96693fab'; + const PREVIEW_PATH = `${DATA_VIEW_SWAP_REFERENCES_PATH}/_preview`; + let dataViewId = ''; + + describe('main', () => { + const kibanaServer = getService('kibanaServer'); + before(async () => { + const result = await supertest + .post(DATA_VIEW_PATH) + .send({ data_view: { title } }) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + dataViewId = result.body.data_view.id; + }); + after(async () => { + await supertest + .delete(SPECIFIC_DATA_VIEW_PATH.replace('{id}', dataViewId)) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + }); + beforeEach(async () => { + await kibanaServer.importExport.load( + 'test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json' + ); + }); + afterEach(async () => { + await kibanaServer.importExport.unload( + 'test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json' + ); + }); + + it('can preview', async () => { + const res = await supertest + .post(PREVIEW_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + fromId: prevDataViewId, + toId: dataViewId, + }); + expect(res).to.have.property('status', 200); + }); + + it('can preview specifying type', async () => { + const res = await supertest + .post(PREVIEW_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + fromId: prevDataViewId, + fromType: 'index-pattern', + toId: dataViewId, + }); + expect(res).to.have.property('status', 200); + }); + + it('can save changes', async () => { + const res = await supertest + .post(DATA_VIEW_SWAP_REFERENCES_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + fromId: prevDataViewId, + toId: dataViewId, + }); + expect(res).to.have.property('status', 200); + expect(res.body.result.length).to.equal(1); + expect(res.body.result[0].id).to.equal('dd7caf20-9efd-11e7-acb3-3dab96693fab'); + expect(res.body.result[0].type).to.equal('visualization'); + }); + + it('can save changes and remove old saved object', async () => { + const res = await supertest + .post(DATA_VIEW_SWAP_REFERENCES_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + fromId: prevDataViewId, + toId: dataViewId, + delete: true, + }); + expect(res).to.have.property('status', 200); + expect(res.body.result.length).to.equal(1); + expect(res.body.deleteStatus.remainingRefs).to.equal(0); + expect(res.body.deleteStatus.deletePerformed).to.equal(true); + + const res2 = await supertest + .get(SPECIFIC_DATA_VIEW_PATH.replace('{id}', prevDataViewId)) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + + expect(res2).to.have.property('statusCode', 404); + }); + + describe('limit affected saved objects', () => { + beforeEach(async () => { + await kibanaServer.importExport.load( + 'test/api_integration/fixtures/kbn_archiver/management/saved_objects/relationships.json' + ); + }); + afterEach(async () => { + await kibanaServer.importExport.unload( + 'test/api_integration/fixtures/kbn_archiver/management/saved_objects/relationships.json' + ); + }); + + it("won't delete if reference remains", async () => { + const res = await supertest + .post(DATA_VIEW_SWAP_REFERENCES_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + fromId: '8963ca30-3224-11e8-a572-ffca06da1357', + toId: '91200a00-9efd-11e7-acb3-3dab96693fab', + forId: ['960372e0-3224-11e8-a572-ffca06da1357'], + delete: true, + }); + expect(res).to.have.property('status', 200); + expect(res.body.result.length).to.equal(1); + expect(res.body.deleteStatus.remainingRefs).to.equal(1); + expect(res.body.deleteStatus.deletePerformed).to.equal(false); + }); + + it('can limit by id', async () => { + // confirm this will find two items + const res = await supertest + .post(PREVIEW_PATH) + .send({ + fromId: '8963ca30-3224-11e8-a572-ffca06da1357', + toId: '91200a00-9efd-11e7-acb3-3dab96693fab', + }) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + expect(res).to.have.property('status', 200); + expect(res.body.result.length).to.equal(2); + + // limit to one item + const res2 = await supertest + .post(DATA_VIEW_SWAP_REFERENCES_PATH) + .send({ + fromId: '8963ca30-3224-11e8-a572-ffca06da1357', + toId: '91200a00-9efd-11e7-acb3-3dab96693fab', + forId: ['960372e0-3224-11e8-a572-ffca06da1357'], + }) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + expect(res2).to.have.property('status', 200); + expect(res2.body.result.length).to.equal(1); + }); + + it('can limit by type', async () => { + // confirm this will find two items + const res = await supertest + .post(PREVIEW_PATH) + .send({ + fromId: '8963ca30-3224-11e8-a572-ffca06da1357', + toId: '91200a00-9efd-11e7-acb3-3dab96693fab', + }) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + expect(res).to.have.property('status', 200); + expect(res.body.result.length).to.equal(2); + + // limit to one item + const res2 = await supertest + .post(DATA_VIEW_SWAP_REFERENCES_PATH) + .send({ + fromId: '8963ca30-3224-11e8-a572-ffca06da1357', + toId: '91200a00-9efd-11e7-acb3-3dab96693fab', + forType: 'search', + }) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()); + expect(res2).to.have.property('status', 200); + expect(res2.body.result.length).to.equal(1); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/index.ts index 4ea6b50bb8f2f..ae68f7ec7670c 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/index.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/index.ts @@ -28,5 +28,11 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./index_management')); loadTestFile(require.resolve('./alerting')); loadTestFile(require.resolve('./ingest_pipelines')); + loadTestFile(require.resolve('./data_view_field_editor')); + loadTestFile(require.resolve('./data_views')); + loadTestFile(require.resolve('./kql_telemetry')); + loadTestFile(require.resolve('./scripts_tests')); + loadTestFile(require.resolve('./search_oss')); + loadTestFile(require.resolve('./search_xpack')); }); } diff --git a/x-pack/test_serverless/api_integration/test_suites/common/kql_telemetry/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/kql_telemetry/index.ts new file mode 100644 index 0000000000000..07b76ff08e58c --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/kql_telemetry/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('KQL', () => { + loadTestFile(require.resolve('./kql_telemetry')); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/kql_telemetry/kql_telemetry.ts b/x-pack/test_serverless/api_integration/test_suites/common/kql_telemetry/kql_telemetry.ts new file mode 100644 index 0000000000000..adbac6e89b548 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/kql_telemetry/kql_telemetry.ts @@ -0,0 +1,156 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { get } from 'lodash'; +import { ANALYTICS_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; +import { KQL_TELEMETRY_ROUTE_LATEST_VERSION } from '@kbn/data-plugin/common'; +import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; +import type { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const kibanaServer = getService('kibanaServer'); + const es = getService('es'); + const svlCommonApi = getService('svlCommonApi'); + + describe('telemetry API', () => { + before(async () => { + // TODO: Clean `kql-telemetry` before running the tests + await kibanaServer.savedObjects.clean({ types: ['kql-telemetry'] }); + await kibanaServer.importExport.load( + 'test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json' + ); + }); + after(async () => { + await kibanaServer.importExport.unload( + 'test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json' + ); + }); + + it('should increment the opt *in* counter in the .kibana_analytics/kql-telemetry document', async () => { + await supertest + .post('/internal/kql_opt_in_stats') + .set('content-type', 'application/json') + .set(ELASTIC_HTTP_VERSION_HEADER, KQL_TELEMETRY_ROUTE_LATEST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ opt_in: true }) + .expect(200); + + return es + .search({ + index: ANALYTICS_SAVED_OBJECT_INDEX, + q: 'type:kql-telemetry', + }) + .then((response) => { + const kqlTelemetryDoc = get(response, 'hits.hits[0]._source.kql-telemetry'); + expect(kqlTelemetryDoc.optInCount).to.be(1); + }); + }); + + it('should increment the opt *out* counter in the .kibana_analytics/kql-telemetry document', async () => { + await supertest + .post('/internal/kql_opt_in_stats') + .set('content-type', 'application/json') + .set(ELASTIC_HTTP_VERSION_HEADER, KQL_TELEMETRY_ROUTE_LATEST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ opt_in: false }) + .expect(200); + + return es + .search({ + index: ANALYTICS_SAVED_OBJECT_INDEX, + q: 'type:kql-telemetry', + }) + .then((response) => { + const kqlTelemetryDoc = get(response, 'hits.hits[0]._source.kql-telemetry'); + expect(kqlTelemetryDoc.optOutCount).to.be(1); + }); + }); + + it('should report success when opt *in* is incremented successfully', () => { + return ( + supertest + .post('/internal/kql_opt_in_stats') + .set('content-type', 'application/json') + .set(ELASTIC_HTTP_VERSION_HEADER, KQL_TELEMETRY_ROUTE_LATEST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ opt_in: true }) + .expect('Content-Type', /json/) + .expect(200) + .then(({ body }) => { + expect(body.success).to.be(true); + }) + ); + }); + + it('should report success when opt *out* is incremented successfully', () => { + return ( + supertest + .post('/internal/kql_opt_in_stats') + .set('content-type', 'application/json') + .set(ELASTIC_HTTP_VERSION_HEADER, KQL_TELEMETRY_ROUTE_LATEST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ opt_in: false }) + .expect('Content-Type', /json/) + .expect(200) + .then(({ body }) => { + expect(body.success).to.be(true); + }) + ); + }); + + it('should only accept literal boolean values for the opt_in POST body param', function () { + return Promise.all([ + supertest + .post('/internal/kql_opt_in_stats') + .set('content-type', 'application/json') + .set(ELASTIC_HTTP_VERSION_HEADER, KQL_TELEMETRY_ROUTE_LATEST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ opt_in: 'notabool' }) + .expect(400), + supertest + .post('/internal/kql_opt_in_stats') + .set('content-type', 'application/json') + .set(ELASTIC_HTTP_VERSION_HEADER, KQL_TELEMETRY_ROUTE_LATEST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ opt_in: 0 }) + .expect(400), + supertest + .post('/internal/kql_opt_in_stats') + .set('content-type', 'application/json') + .set(ELASTIC_HTTP_VERSION_HEADER, KQL_TELEMETRY_ROUTE_LATEST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ opt_in: null }) + .expect(400), + supertest + .post('/internal/kql_opt_in_stats') + .set('content-type', 'application/json') + .set(ELASTIC_HTTP_VERSION_HEADER, KQL_TELEMETRY_ROUTE_LATEST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ opt_in: undefined }) + .expect(400), + supertest + .post('/internal/kql_opt_in_stats') + .set('content-type', 'application/json') + .set(ELASTIC_HTTP_VERSION_HEADER, KQL_TELEMETRY_ROUTE_LATEST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({}) + .expect(400), + ]); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/scripts_tests/index.js b/x-pack/test_serverless/api_integration/test_suites/common/scripts_tests/index.js new file mode 100644 index 0000000000000..d1eeb009a7cce --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/scripts_tests/index.js @@ -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 default function ({ loadTestFile }) { + // TODO: The `scripts` folder was renamed to `scripts_tests` because the folder + // name `scripts` triggers the `eslint@kbn/imports/no_boundary_crossing` rule + describe('scripts', () => { + loadTestFile(require.resolve('./languages')); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/scripts_tests/languages.js b/x-pack/test_serverless/api_integration/test_suites/common/scripts_tests/languages.js new file mode 100644 index 0000000000000..832bf6df49188 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/scripts_tests/languages.js @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; + +import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; +import { SCRIPT_LANGUAGES_ROUTE_LATEST_VERSION } from '@kbn/data-plugin/common/constants'; + +export default function ({ getService }) { + const supertest = getService('supertest'); + const svlCommonApi = getService('svlCommonApi'); + + describe('Script Languages API', function getLanguages() { + it('should return 200 with an array of languages', () => + supertest + .get('/internal/scripts/languages') + .set(ELASTIC_HTTP_VERSION_HEADER, SCRIPT_LANGUAGES_ROUTE_LATEST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .expect(200) + .then((response) => { + expect(response.body).to.be.an('array'); + })); + + // eslint-disable-next-line jest/no-disabled-tests + it.skip('should only return langs enabled for inline scripting', () => + supertest + .get('/internal/scripts/languages') + .set(ELASTIC_HTTP_VERSION_HEADER, SCRIPT_LANGUAGES_ROUTE_LATEST_VERSION) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .expect(200) + .then((response) => { + expect(response.body).to.contain('expression'); + expect(response.body).to.contain('painless'); + expect(response.body).to.not.contain('groovy'); + })); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/search_oss/bsearch.ts b/x-pack/test_serverless/api_integration/test_suites/common/search_oss/bsearch.ts new file mode 100644 index 0000000000000..ba1f974a01608 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/search_oss/bsearch.ts @@ -0,0 +1,249 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import request from 'superagent'; +import { inflateResponse } from '@kbn/bfetch-plugin/public/streaming'; +import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; +import { BFETCH_ROUTE_VERSION_LATEST } from '@kbn/bfetch-plugin/common'; +import type { FtrProviderContext } from '../../../ftr_provider_context'; +import { painlessErrReq } from './painless_err_req'; +import { verifyErrorResponse } from './verify_error'; + +function parseBfetchResponse(resp: request.Response, compressed: boolean = false) { + return resp.text + .trim() + .split('\n') + .map((item) => { + return JSON.parse(compressed ? inflateResponse(item) : item); + }); +} + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const svlCommonApi = getService('svlCommonApi'); + + describe('bsearch', () => { + describe('post', () => { + it('should return 200 a single response', async () => { + const resp = await supertest + .post(`/internal/bsearch`) + .set(ELASTIC_HTTP_VERSION_HEADER, BFETCH_ROUTE_VERSION_LATEST) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + batch: [ + { + request: { + params: { + index: '.kibana', + body: { + query: { + match_all: {}, + }, + }, + }, + }, + options: { + strategy: 'es', + }, + }, + ], + }); + + const jsonBody = parseBfetchResponse(resp); + + expect(resp.status).to.be(200); + expect(jsonBody[0].id).to.be(0); + expect(jsonBody[0].result.isPartial).to.be(false); + expect(jsonBody[0].result.isRunning).to.be(false); + expect(jsonBody[0].result).to.have.property('rawResponse'); + }); + + it('should return 200 a single response from compressed', async () => { + const resp = await supertest + .post(`/internal/bsearch?compress=true`) + .set(ELASTIC_HTTP_VERSION_HEADER, BFETCH_ROUTE_VERSION_LATEST) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + batch: [ + { + request: { + params: { + index: '.kibana', + body: { + query: { + match_all: {}, + }, + }, + }, + }, + options: { + strategy: 'es', + }, + }, + ], + }); + + const jsonBody = parseBfetchResponse(resp, true); + + expect(resp.status).to.be(200); + expect(jsonBody[0].id).to.be(0); + expect(jsonBody[0].result.isPartial).to.be(false); + expect(jsonBody[0].result.isRunning).to.be(false); + expect(jsonBody[0].result).to.have.property('rawResponse'); + }); + + it('should return a batch of successful responses', async () => { + const resp = await supertest + .post(`/internal/bsearch`) + .set(ELASTIC_HTTP_VERSION_HEADER, BFETCH_ROUTE_VERSION_LATEST) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + batch: [ + { + request: { + params: { + index: '.kibana', + body: { + query: { + match_all: {}, + }, + }, + }, + }, + }, + { + request: { + params: { + index: '.kibana', + body: { + query: { + match_all: {}, + }, + }, + }, + }, + }, + ], + }); + + expect(resp.status).to.be(200); + const parsedResponse = parseBfetchResponse(resp); + expect(parsedResponse).to.have.length(2); + parsedResponse.forEach((responseJson) => { + expect(responseJson.result).to.have.property('isPartial'); + expect(responseJson.result).to.have.property('isRunning'); + expect(responseJson.result).to.have.property('rawResponse'); + }); + }); + + it('should return error for not found strategy', async () => { + const resp = await supertest + .post(`/internal/bsearch`) + .set(ELASTIC_HTTP_VERSION_HEADER, BFETCH_ROUTE_VERSION_LATEST) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + batch: [ + { + request: { + params: { + index: '.kibana', + body: { + query: { + match_all: {}, + }, + }, + }, + }, + options: { + strategy: 'wtf', + }, + }, + ], + }); + + expect(resp.status).to.be(200); + parseBfetchResponse(resp).forEach((responseJson, i) => { + expect(responseJson.id).to.be(i); + verifyErrorResponse(responseJson.error, 404, 'Search strategy wtf not found'); + }); + }); + + it('should return 400 when index type is provided in "es" strategy', async () => { + const resp = await supertest + .post(`/internal/bsearch`) + .set(ELASTIC_HTTP_VERSION_HEADER, BFETCH_ROUTE_VERSION_LATEST) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + batch: [ + { + request: { + index: '.kibana', + indexType: 'baad', + params: { + body: { + query: { + match_all: {}, + }, + }, + }, + }, + options: { + strategy: 'es', + }, + }, + ], + }); + + expect(resp.status).to.be(200); + parseBfetchResponse(resp).forEach((responseJson, i) => { + expect(responseJson.id).to.be(i); + verifyErrorResponse(responseJson.error, 400, 'Unsupported index pattern type baad'); + }); + }); + + describe('painless', () => { + before(async () => { + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); + }); + + after(async () => { + await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); + }); + it('should return 400 "search_phase_execution_exception" for Painless error in "es" strategy', async () => { + const resp = await supertest + .post(`/internal/bsearch`) + .set(ELASTIC_HTTP_VERSION_HEADER, BFETCH_ROUTE_VERSION_LATEST) + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + batch: [ + { + request: painlessErrReq, + options: { + strategy: 'es', + }, + }, + ], + }); + + expect(resp.status).to.be(200); + parseBfetchResponse(resp).forEach((responseJson, i) => { + expect(responseJson.id).to.be(i); + verifyErrorResponse(responseJson.error, 400, 'search_phase_execution_exception', true); + }); + }); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/search_oss/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/search_oss/index.ts new file mode 100644 index 0000000000000..598493bfd2182 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/search_oss/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + // TODO: This `search` folder was renamed to `search_oss` to + // differentiate it from the x-pack `search` folder (now `search_xpack`) + describe('search', () => { + loadTestFile(require.resolve('./search')); + // TODO: Removed `sql_search` since + // SQL is not supported in Serverless + loadTestFile(require.resolve('./bsearch')); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/search_oss/painless_err_req.ts b/x-pack/test_serverless/api_integration/test_suites/common/search_oss/painless_err_req.ts new file mode 100644 index 0000000000000..3e722770c5ae1 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/search_oss/painless_err_req.ts @@ -0,0 +1,43 @@ +/* + * 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 painlessErrReq = { + params: { + index: 'log*', + body: { + size: 500, + fields: ['*'], + script_fields: { + invalid_scripted_field: { + script: { + source: 'invalid', + lang: 'painless', + }, + }, + }, + stored_fields: ['*'], + query: { + bool: { + filter: [ + { + match_all: {}, + }, + { + range: { + '@timestamp': { + gte: '2015-01-19T12:27:55.047Z', + lte: '2021-01-19T12:27:55.047Z', + format: 'strict_date_optional_time', + }, + }, + }, + ], + }, + }, + }, + }, +}; diff --git a/x-pack/test_serverless/api_integration/test_suites/common/search_oss/search.ts b/x-pack/test_serverless/api_integration/test_suites/common/search_oss/search.ts new file mode 100644 index 0000000000000..85a57a2c2d272 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/search_oss/search.ts @@ -0,0 +1,203 @@ +/* + * 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 { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; +import expect from '@kbn/expect'; +import type { FtrProviderContext } from '../../../ftr_provider_context'; +import { painlessErrReq } from './painless_err_req'; +import { verifyErrorResponse } from './verify_error'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const svlCommonApi = getService('svlCommonApi'); + const kibanaServer = getService('kibanaServer'); + + describe('search', () => { + before(async () => { + // TODO: emptyKibanaIndex fails in Serverless with + // "index_not_found_exception: no such index [.kibana_ingest]", + // so it was switched to `savedObjects.cleanStandardList()` + await kibanaServer.savedObjects.cleanStandardList(); + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); + }); + + after(async () => { + await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); + }); + describe('post', () => { + it('should return 200 when correctly formatted searches are provided', async () => { + const resp = await supertest + .post(`/internal/search/es`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + params: { + body: { + query: { + match_all: {}, + }, + }, + }, + }) + .expect(200); + + expect(resp.status).to.be(200); + expect(resp.body.isPartial).to.be(false); + expect(resp.body.isRunning).to.be(false); + expect(resp.body).to.have.property('rawResponse'); + expect(resp.header).to.have.property(ELASTIC_HTTP_VERSION_HEADER, '1'); + }); + + it('should return 200 if terminated early', async () => { + const resp = await supertest + .post(`/internal/search/es`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + params: { + terminateAfter: 1, + index: 'log*', + size: 1000, + body: { + query: { + match_all: {}, + }, + }, + }, + }) + .expect(200); + + expect(resp.status).to.be(200); + expect(resp.body.isPartial).to.be(false); + expect(resp.body.isRunning).to.be(false); + expect(resp.body.rawResponse.terminated_early).to.be(true); + expect(resp.header).to.have.property(ELASTIC_HTTP_VERSION_HEADER, '1'); + }); + + it('should return 404 when if no strategy is provided', async () => { + const resp = await supertest + .post(`/internal/search`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + body: { + query: { + match_all: {}, + }, + }, + }) + .expect(404); + + verifyErrorResponse(resp.body, 404); + }); + + it('should return 404 when if unknown strategy is provided', async () => { + const resp = await supertest + .post(`/internal/search/banana`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + body: { + query: { + match_all: {}, + }, + }, + }) + .expect(404); + + verifyErrorResponse(resp.body, 404); + expect(resp.body.message).to.contain('banana not found'); + expect(resp.header).to.have.property(ELASTIC_HTTP_VERSION_HEADER, '1'); + }); + + it('should return 400 with illegal ES argument', async () => { + const resp = await supertest + .post(`/internal/search/es`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + params: { + timeout: 1, // This should be a time range string! + index: 'log*', + size: 1000, + body: { + query: { + match_all: {}, + }, + }, + }, + }) + .expect(400); + + verifyErrorResponse(resp.body, 400, 'illegal_argument_exception', true); + }); + + it('should return 400 with a bad body', async () => { + const resp = await supertest + .post(`/internal/search/es`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send({ + params: { + body: { + index: 'nope nope', + bad_query: [], + }, + }, + }) + .expect(400); + + verifyErrorResponse(resp.body, 400, 'parsing_exception', true); + }); + + it('should return 400 for a painless error', async () => { + const resp = await supertest + .post(`/internal/search/es`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send(painlessErrReq) + .expect(400); + + verifyErrorResponse(resp.body, 400, 'search_phase_execution_exception', true); + }); + }); + + describe('delete', () => { + it('should return 404 when no search id provided', async () => { + const resp = await supertest + .delete(`/internal/search/es`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send() + .expect(404); + verifyErrorResponse(resp.body, 404); + }); + + it('should return 400 when trying a delete on a non supporting strategy', async () => { + const resp = await supertest + .delete(`/internal/search/es/123`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .send() + .expect(400); + verifyErrorResponse(resp.body, 400); + expect(resp.body.message).to.contain("Search strategy es doesn't support cancellations"); + expect(resp.header).to.have.property(ELASTIC_HTTP_VERSION_HEADER, '1'); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/search_oss/verify_error.ts b/x-pack/test_serverless/api_integration/test_suites/common/search_oss/verify_error.ts new file mode 100644 index 0000000000000..3523cefc8b33f --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/search_oss/verify_error.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; + +export const verifyErrorResponse = ( + r: any, + expectedCode: number, + message?: string, + shouldHaveAttrs?: boolean +) => { + expect(r.statusCode).to.be(expectedCode); + if (message) { + expect(r.message).to.include.string(message); + } + if (shouldHaveAttrs) { + expect(r).to.have.property('attributes'); + expect(r.attributes).to.have.property('root_cause'); + } else { + expect(r).not.to.have.property('attributes'); + } +}; diff --git a/x-pack/test_serverless/api_integration/test_suites/common/search_xpack/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/search_xpack/index.ts new file mode 100644 index 0000000000000..fc433f4655977 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/search_xpack/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + // TODO: This `search` folder was renamed to `search_xpack` to + // differentiate it from the oss `search` folder (now `search_oss`) + describe('search', () => { + loadTestFile(require.resolve('./search')); + // TODO: Removed `session` since search + // sessions are not supported in Serverless + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/search_xpack/search.ts b/x-pack/test_serverless/api_integration/test_suites/common/search_xpack/search.ts new file mode 100644 index 0000000000000..1a4839cbae6fa --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/search_xpack/search.ts @@ -0,0 +1,501 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { parse as parseCookie } from 'tough-cookie'; +import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; +import { omit } from 'lodash'; +import type { FtrProviderContext } from '../../../ftr_provider_context'; +import { verifyErrorResponse } from '../search_oss/verify_error'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const es = getService('es'); + const log = getService('log'); + const retry = getService('retry'); + const security = getService('security'); + // TODO: `supertestWithoutAuth` is typed as `any` in `x-pack/test/api_integration/apis/search/search.ts`, + // but within Serverless tests it's typed as `supertest.SuperTest`. This causes TS errors + // when accessing `loginResponse.headers`, so we cast it as `any` here to match the original tests. + const supertestNoAuth = getService('supertestWithoutAuth') as any; + const svlCommonApi = getService('svlCommonApi'); + + const shardDelayAgg = (delay: string) => ({ + aggs: { + delay: { + shard_delay: { + value: delay, + }, + }, + }, + }); + + async function markRequiresShardDelayAgg(testContext: Mocha.Context) { + const body = await es.info(); + if (!body.version.number.includes('SNAPSHOT')) { + log.debug('Skipping because this build does not have the required shard_delay agg'); + testContext.skip(); + } + } + + describe('search', () => { + before(async () => { + // ensure es not empty + await es.index({ + index: 'search-api-test', + id: 'search-api-test-doc', + body: { message: 'test doc' }, + refresh: 'wait_for', + }); + }); + after(async () => { + await es.indices.delete({ + index: 'search-api-test', + }); + }); + + describe('post', () => { + it('should return 200 with final response without search id if wait_for_completion_timeout is long enough', async function () { + const resp = await supertest + .post(`/internal/search/ese`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .set('kbn-xsrf', 'foo') + .send({ + params: { + body: { + query: { + match_all: {}, + }, + }, + wait_for_completion_timeout: '10s', + }, + }) + .expect(200); + + const { id } = resp.body; + expect(id).to.be(undefined); + expect(resp.body.isPartial).to.be(false); + expect(resp.body.isRunning).to.be(false); + expect(resp.body).to.have.property('rawResponse'); + }); + + it('should return 200 with search id and partial response if wait_for_completion_timeout is not long enough', async function () { + await markRequiresShardDelayAgg(this); + + const resp = await supertest + .post(`/internal/search/ese`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .set('kbn-xsrf', 'foo') + .send({ + params: { + body: { + query: { + match_all: {}, + }, + ...shardDelayAgg('3s'), + }, + wait_for_completion_timeout: '1ms', + }, + }) + .expect(200); + + const { id } = resp.body; + expect(id).not.to.be(undefined); + expect(resp.body.isPartial).to.be(true); + expect(resp.body.isRunning).to.be(true); + expect(resp.body).to.have.property('rawResponse'); + }); + + it('should retrieve results from completed search with search id', async function () { + await markRequiresShardDelayAgg(this); + + const resp = await supertest + .post(`/internal/search/ese`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .set('kbn-xsrf', 'foo') + .send({ + params: { + body: { + query: { + match_all: {}, + }, + ...shardDelayAgg('3s'), + }, + wait_for_completion_timeout: '1ms', + }, + }) + .expect(200); + + const { id } = resp.body; + expect(id).not.to.be(undefined); + expect(resp.body.isPartial).to.be(true); + expect(resp.body.isRunning).to.be(true); + + await new Promise((resolve) => setTimeout(resolve, 3000)); + + await retry.tryForTime(10000, async () => { + const resp2 = await supertest + .post(`/internal/search/ese/${id}`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .set('kbn-xsrf', 'foo') + .send({}) + .expect(200); + + expect(resp2.body.id).not.to.be(undefined); + expect(resp2.body.isPartial).to.be(false); + expect(resp2.body.isRunning).to.be(false); + + return true; + }); + }); + + it('should retrieve results from in-progress search with search id', async function () { + await markRequiresShardDelayAgg(this); + + const resp = await supertest + .post(`/internal/search/ese`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .set('kbn-xsrf', 'foo') + .send({ + params: { + body: { + query: { + match_all: {}, + }, + ...shardDelayAgg('10s'), + }, + wait_for_completion_timeout: '1ms', + }, + }) + .expect(200); + + const { id } = resp.body; + expect(id).not.to.be(undefined); + expect(resp.body.isPartial).to.be(true); + expect(resp.body.isRunning).to.be(true); + + const resp2 = await supertest + .post(`/internal/search/ese/${id}`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .set('kbn-xsrf', 'foo') + .send({}) + .expect(200); + + expect(resp2.body.id).not.to.be(undefined); + expect(resp2.body.isPartial).to.be(true); + expect(resp2.body.isRunning).to.be(true); + }); + + it('should fail without kbn-xref header', async () => { + const resp = await supertest + .post(`/internal/search/ese`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(omit(svlCommonApi.getInternalRequestHeader(), 'kbn-xsrf')) + .send({ + params: { + body: { + query: { + match_all: {}, + }, + }, + }, + }) + .expect(400); + + verifyErrorResponse(resp.body, 400, 'Request must contain a kbn-xsrf header.'); + }); + + it('should return 400 when unknown index type is provided', async () => { + const resp = await supertest + .post(`/internal/search/ese`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .set('kbn-xsrf', 'foo') + .send({ + indexType: 'baad', + params: { + body: { + query: { + match_all: {}, + }, + }, + }, + }) + .expect(400); + + verifyErrorResponse(resp.body, 400, 'Unknown indexType'); + }); + + it('should return 400 if invalid id is provided', async () => { + const resp = await supertest + .post(`/internal/search/ese/123`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .set('kbn-xsrf', 'foo') + .send({ + params: { + body: { + query: { + match_all: {}, + }, + }, + }, + }) + .expect(400); + + verifyErrorResponse(resp.body, 400, 'illegal_argument_exception', true); + }); + + it('should return 404 if unknown id is provided', async () => { + const resp = await supertest + .post( + `/internal/search/ese/FkxOb21iV1g2VGR1S2QzaWVtRU9fMVEbc3JWeWc1VHlUdDZ6MENxcXlYVG1Fdzo2NDg4` + ) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .set('kbn-xsrf', 'foo') + .send({ + params: { + body: { + query: { + match_all: {}, + }, + }, + }, + }) + .expect(404); + verifyErrorResponse(resp.body, 404, 'resource_not_found_exception', true); + }); + + it('should return 400 with a bad body', async () => { + const resp = await supertest + .post(`/internal/search/ese`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .set('kbn-xsrf', 'foo') + .send({ + params: { + body: { + index: 'nope nope', + bad_query: [], + }, + }, + }) + .expect(400); + + verifyErrorResponse(resp.body, 400, 'parsing_exception', true); + }); + + // TODO: Security works differently in Serverless so this test fails, + // we'll need to figure out how to test this properly in Serverless + it.skip('should return 403 for lack of privledges', async () => { + const username = 'no_access'; + const password = 't0pS3cr3t'; + + await security.user.create(username, { + password, + roles: ['test_shakespeare_reader'], + }); + + const loginResponse = await supertestNoAuth + .post('/internal/security/login') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .set('kbn-xsrf', 'xxx') + .send({ + providerType: 'basic', + providerName: 'basic', + currentURL: '/', + params: { username, password }, + }) + .expect(200); + + const sessionCookie = parseCookie(loginResponse.headers['set-cookie'][0]); + + await supertestNoAuth + .post(`/internal/search/ese`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .set('kbn-xsrf', 'foo') + .set('Cookie', sessionCookie!.cookieString()) + .send({ + params: { + index: 'log*', + body: { + query: { + match_all: {}, + }, + }, + wait_for_completion_timeout: '10s', + }, + }) + .expect(403); + + await security.testUser.restoreDefaults(); + }); + }); + + // TODO: Removed rollup tests since rollups aren't supported in Serverless + + describe('delete', () => { + it('should return 404 when no search id provided', async () => { + await supertest + .delete(`/internal/search/ese`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .set('kbn-xsrf', 'foo') + .send() + .expect(404); + }); + + it('should return 400 when trying a delete a bad id', async () => { + const resp = await supertest + .delete(`/internal/search/ese/123`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .set('kbn-xsrf', 'foo') + .send() + .expect(400); + verifyErrorResponse(resp.body, 400, 'illegal_argument_exception', true); + }); + + it('should delete an in-progress search', async function () { + await markRequiresShardDelayAgg(this); + + const resp = await supertest + .post(`/internal/search/ese`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .set('kbn-xsrf', 'foo') + .send({ + params: { + body: { + query: { + match_all: {}, + }, + ...shardDelayAgg('10s'), + }, + wait_for_completion_timeout: '1ms', + }, + }) + .expect(200); + + const { id } = resp.body; + expect(id).not.to.be(undefined); + expect(resp.body.isPartial).to.be(true); + expect(resp.body.isRunning).to.be(true); + + await supertest + .delete(`/internal/search/ese/${id}`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .set('kbn-xsrf', 'foo') + .send() + .expect(200); + + // try to re-fetch + await supertest + .post(`/internal/search/ese/${id}`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .set('kbn-xsrf', 'foo') + .send({}) + .expect(404); + }); + + // FLAKY: https://github.com/elastic/kibana/issues/164856 + it.skip('should delete a completed search', async function () { + await markRequiresShardDelayAgg(this); + + const resp = await supertest + .post(`/internal/search/ese`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .set('kbn-xsrf', 'foo') + .send({ + params: { + body: { + query: { + match_all: {}, + }, + ...shardDelayAgg('3s'), + }, + wait_for_completion_timeout: '1ms', + }, + }) + .expect(200); + + const { id } = resp.body; + expect(id).not.to.be(undefined); + expect(resp.body.isPartial).to.be(true); + expect(resp.body.isRunning).to.be(true); + + await new Promise((resolve) => setTimeout(resolve, 3000)); + + await retry.tryForTime(10000, async () => { + const resp2 = await supertest + .post(`/internal/search/ese/${id}`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .set('kbn-xsrf', 'foo') + .send({}) + .expect(200); + + expect(resp2.body.id).not.to.be(undefined); + expect(resp2.body.isPartial).to.be(false); + expect(resp2.body.isRunning).to.be(false); + + return true; + }); + + await supertest + .delete(`/internal/search/ese/${id}`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .set('kbn-xsrf', 'foo') + .send() + .expect(200); + + // try to re-fetch + await supertest + .post(`/internal/search/ese/${id}`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + // TODO: API requests in Serverless require internal request headers + .set(svlCommonApi.getInternalRequestHeader()) + .set('kbn-xsrf', 'foo') + .send({}) + .expect(404); + }); + }); + }); +} diff --git a/x-pack/test_serverless/functional/test_suites/common/management.ts b/x-pack/test_serverless/functional/test_suites/common/management.ts index 504325ff363b1..fe2561854c49a 100644 --- a/x-pack/test_serverless/functional/test_suites/common/management.ts +++ b/x-pack/test_serverless/functional/test_suites/common/management.ts @@ -54,6 +54,18 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { appName: 'Watcher', url: 'insightsAndAlerting/watcher', }, + { + appName: 'Users', + url: 'security/users', + }, + { + appName: 'Roles', + url: 'security/roles', + }, + { + appName: 'Role Mappings', + url: 'security/role_mappings', + }, ]; DISABLED_PLUGINS.forEach(({ appName, url }) => { diff --git a/x-pack/test_serverless/functional/test_suites/common/security/api_keys.ts b/x-pack/test_serverless/functional/test_suites/common/security/api_keys.ts new file mode 100644 index 0000000000000..ab650c4c43a25 --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/common/security/api_keys.ts @@ -0,0 +1,457 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { Client } from '@elastic/elasticsearch'; +import { ToolingLog } from '@kbn/tooling-log'; +import type { estypes } from '@elastic/elasticsearch'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +async function clearAllApiKeys(esClient: Client, logger: ToolingLog) { + const existingKeys = await esClient.security.queryApiKeys(); + if (existingKeys.count > 0) { + await Promise.all( + existingKeys.api_keys.map(async (key) => { + esClient.security.invalidateApiKey({ ids: [key.id] }); + }) + ); + } else { + logger.debug('No API keys to delete.'); + } +} + +export default ({ getPageObjects, getService }: FtrProviderContext) => { + const es = getService('es'); + const pageObjects = getPageObjects(['common', 'apiKeys']); + const log = getService('log'); + const security = getService('security'); + const testSubjects = getService('testSubjects'); + const find = getService('find'); + const browser = getService('browser'); + const retry = getService('retry'); + + const testRoles: Record = { + viewer: { + cluster: ['all'], + indices: [ + { + names: ['*'], + privileges: ['all'], + allow_restricted_indices: false, + }, + { + names: ['*'], + privileges: ['monitor', 'read', 'view_index_metadata', 'read_cross_cluster'], + allow_restricted_indices: true, + }, + ], + run_as: ['*'], + }, + }; + + const otherUser: estypes.SecurityPutUserRequest = { + username: 'other_user', + password: 'changeme', + roles: ['superuser'], + }; + + async function ensureApiKeysExist(apiKeysNames: string[]) { + await retry.try(async () => { + for (const apiKeyName of apiKeysNames) { + log.debug(`Checking if API key ("${apiKeyName}") exists.`); + await pageObjects.apiKeys.ensureApiKeyExists(apiKeyName); + log.debug(`API key ("${apiKeyName}") exists.`); + } + }); + } + + describe('Home page', function () { + before(async () => { + await clearAllApiKeys(es, log); + await security.testUser.setRoles(['kibana_admin']); + await es.security.putUser(otherUser); + + await pageObjects.common.navigateToUrl('management', 'security/api_keys', { + shouldUseHashForSubUrl: false, + }); + }); + + after(async () => { + await es.security.deleteUser({ username: otherUser.username }); + await security.testUser.restoreDefaults(); + }); + + // https://www.elastic.co/guide/en/kibana/7.6/api-keys.html#api-keys-security-privileges + it('Hides management link if user is not authorized', async () => { + await testSubjects.missingOrFail('apiKeys'); + }); + + it('Loads the app', async () => { + await security.testUser.setRoles(['test_api_keys']); + log.debug('Checking for Create API key call to action'); + await find.existsByLinkText('Create API key'); + }); + + describe('creates API key', function () { + before(async () => { + await security.testUser.setRoles(['kibana_admin', 'test_api_keys']); + await pageObjects.common.navigateToUrl('management', 'security/api_keys', { + shouldUseHashForSubUrl: false, + }); + + // Delete any API keys created outside of these tests + await pageObjects.apiKeys.bulkDeleteApiKeys(); + }); + + afterEach(async () => { + await pageObjects.apiKeys.deleteAllApiKeyOneByOne(); + }); + + after(async () => { + await clearAllApiKeys(es, log); + }); + + it('when submitting form, close dialog and displays new api key', async () => { + const apiKeyName = 'Happy API Key'; + await pageObjects.apiKeys.clickOnPromptCreateApiKey(); + expect(await browser.getCurrentUrl()).to.contain('app/management/security/api_keys/create'); + expect(await pageObjects.apiKeys.getFlyoutTitleText()).to.be('Create API key'); + + await pageObjects.apiKeys.setApiKeyName(apiKeyName); + await pageObjects.apiKeys.clickSubmitButtonOnApiKeyFlyout(); + const newApiKeyCreation = await pageObjects.apiKeys.getNewApiKeyCreation(); + + expect(await browser.getCurrentUrl()).to.not.contain( + 'app/management/security/api_keys/flyout' + ); + expect(await browser.getCurrentUrl()).to.contain('app/management/security/api_keys'); + expect(await pageObjects.apiKeys.isApiKeyModalExists()).to.be(false); + expect(newApiKeyCreation).to.be(`Created API key '${apiKeyName}'`); + }); + + it('with optional expiration, redirects back and displays base64', async () => { + const apiKeyName = 'Happy expiration API key'; + await pageObjects.apiKeys.clickOnPromptCreateApiKey(); + expect(await browser.getCurrentUrl()).to.contain('app/management/security/api_keys/create'); + + await pageObjects.apiKeys.setApiKeyName(apiKeyName); + await pageObjects.apiKeys.toggleCustomExpiration(); + await pageObjects.apiKeys.setApiKeyCustomExpiration('12'); + await pageObjects.apiKeys.clickSubmitButtonOnApiKeyFlyout(); + const newApiKeyCreation = await pageObjects.apiKeys.getNewApiKeyCreation(); + + expect(await browser.getCurrentUrl()).to.not.contain( + 'app/management/security/api_keys/create' + ); + expect(await browser.getCurrentUrl()).to.contain('app/management/security/api_keys'); + expect(await pageObjects.apiKeys.isApiKeyModalExists()).to.be(false); + expect(newApiKeyCreation).to.be(`Created API key '${apiKeyName}'`); + }); + }); + + describe('Update API key', function () { + before(async () => { + await security.testUser.setRoles(['kibana_admin', 'test_api_keys']); + await pageObjects.common.navigateToUrl('management', 'security/api_keys', { + shouldUseHashForSubUrl: false, + }); + + // Delete any API keys created outside these tests + await pageObjects.apiKeys.bulkDeleteApiKeys(); + }); + + afterEach(async () => { + await pageObjects.apiKeys.deleteAllApiKeyOneByOne(); + }); + + after(async () => { + await clearAllApiKeys(es, log); + }); + + it('should create a new API key, click the name of the new row, fill out and submit form, and display success message', async () => { + // Create a key to updated + const apiKeyName = 'Happy API Key to Update'; + + await es.security.grantApiKey({ + api_key: { + name: apiKeyName, + expiration: '1d', + }, + grant_type: 'password', + run_as: 'elastic', + username: 'elastic', + password: 'changeme', + }); + + await browser.refresh(); + + log.debug('API key created, moving on to update'); + + // Update newly created API Key + await pageObjects.apiKeys.clickExistingApiKeyToOpenFlyout(apiKeyName); + + expect(await browser.getCurrentUrl()).to.contain('app/management/security/api_keys'); + + await pageObjects.apiKeys.waitForSubmitButtonOnApiKeyFlyoutEnabled(); + + expect(await pageObjects.apiKeys.getFlyoutTitleText()).to.be('Update API key'); + + // Verify name input box are disabled + const apiKeyNameInput = await pageObjects.apiKeys.getApiKeyName(); + expect(await apiKeyNameInput.isEnabled()).to.be(false); + + // Status should be displayed + const apiKeyStatus = await pageObjects.apiKeys.getFlyoutApiKeyStatus(); + expect(await apiKeyStatus).to.be('Expires in a day'); + + // Verify metadata is editable + const apiKeyMetadataSwitch = await pageObjects.apiKeys.getMetadataSwitch(); + expect(await apiKeyMetadataSwitch.isEnabled()).to.be(true); + + // Verify restrict privileges is editable + const apiKeyRestrictPrivilegesSwitch = + await pageObjects.apiKeys.getRestrictPrivilegesSwitch(); + expect(await apiKeyRestrictPrivilegesSwitch.isEnabled()).to.be(true); + + // Toggle restrict privileges so the code editor shows up + await apiKeyRestrictPrivilegesSwitch.click(); + + // Toggle metadata switch so the code editor shows up + await apiKeyMetadataSwitch.click(); + + // Check default value of metadata and set value + const restrictPrivilegesCodeEditorValue = + await pageObjects.apiKeys.getCodeEditorValueByIndex(0); + expect(restrictPrivilegesCodeEditorValue).to.be('{}'); + + // Check default value of metadata and set value + const metadataCodeEditorValue = await pageObjects.apiKeys.getCodeEditorValueByIndex(1); + expect(metadataCodeEditorValue).to.be('{}'); + + await pageObjects.apiKeys.setCodeEditorValueByIndex(0, JSON.stringify(testRoles)); + + await pageObjects.apiKeys.setCodeEditorValueByIndex(1, '{"name":"metadataTest"}'); + + // Submit values to update API key + await pageObjects.apiKeys.clickSubmitButtonOnApiKeyFlyout(); + + // Get success message + const updatedApiKeyToastText = await pageObjects.apiKeys.getApiKeyUpdateSuccessToast(); + expect(updatedApiKeyToastText).to.be(`Updated API key '${apiKeyName}'`); + + expect(await browser.getCurrentUrl()).to.contain('app/management/security/api_keys'); + expect(await pageObjects.apiKeys.isApiKeyModalExists()).to.be(false); + }); + }); + + describe('Readonly API key', function () { + before(async () => { + await security.role.create('read_security_role', { + elasticsearch: { + cluster: ['read_security'], + }, + kibana: [ + { + feature: { + infrastructure: ['read'], + }, + spaces: ['*'], + }, + ], + }); + + await security.testUser.setRoles(['kibana_admin', 'test_api_keys']); + await pageObjects.common.navigateToUrl('management', 'security/api_keys', { + shouldUseHashForSubUrl: false, + }); + + // Delete any API keys created outside these tests + await pageObjects.apiKeys.bulkDeleteApiKeys(); + }); + + afterEach(async () => { + await pageObjects.apiKeys.deleteAllApiKeyOneByOne(); + }); + + after(async () => { + await clearAllApiKeys(es, log); + }); + + // Serverless tests run as elastic (superuser) so unable to test `read_security` permissions + it.skip('should see readonly form elements', async () => { + // Create a key to updated + const apiKeyName = 'Happy API Key to View'; + + await es.security.grantApiKey({ + api_key: { + name: apiKeyName, + expiration: '1d', + metadata: { name: 'metadatatest' }, + role_descriptors: { ...testRoles }, + }, + grant_type: 'password', + run_as: 'elastic', + username: 'elastic', + password: 'changeme', + }); + + await browser.refresh(); + + log.debug('API key created, moving on to view'); + + // Set testUsers roles to have the `read_security` cluster privilege + await security.testUser.setRoles(['read_security_role']); + + // View newly created API Key + await pageObjects.apiKeys.clickExistingApiKeyToOpenFlyout(apiKeyName); + expect(await browser.getCurrentUrl()).to.contain('app/management/security/api_keys'); + expect(await pageObjects.apiKeys.getFlyoutTitleText()).to.be('API key details'); + + // Verify name input box are disabled + const apiKeyNameInput = await pageObjects.apiKeys.getApiKeyName(); + expect(await apiKeyNameInput.isEnabled()).to.be(false); + + // Status should be displayed + const apiKeyStatus = await pageObjects.apiKeys.getFlyoutApiKeyStatus(); + expect(await apiKeyStatus).to.be('Expires in a day'); + + const apiKeyMetadataSwitch = await pageObjects.apiKeys.getMetadataSwitch(); + const apiKeyRestrictPrivilegesSwitch = + await pageObjects.apiKeys.getRestrictPrivilegesSwitch(); + + // Verify metadata and restrict privileges switches are now disabled + expect(await apiKeyMetadataSwitch.isEnabled()).to.be(false); + expect(await apiKeyRestrictPrivilegesSwitch.isEnabled()).to.be(false); + + // Close flyout with cancel + await pageObjects.apiKeys.clickCancelButtonOnApiKeyFlyout(); + + // Undo `read_security_role` + await security.testUser.setRoles(['kibana_admin', 'test_api_keys']); + }); + + it('should show the `API key details` flyout if the expiration date is passed', async () => { + const apiKeyName = 'expired-key'; + + await es.security.grantApiKey({ + api_key: { + name: apiKeyName, + expiration: '1ms', + }, + grant_type: 'password', + run_as: 'elastic', + username: 'elastic', + password: 'changeme', + }); + + await browser.refresh(); + + log.debug('API key created, moving on to view'); + + await pageObjects.apiKeys.clickExistingApiKeyToOpenFlyout(apiKeyName); + + expect(await browser.getCurrentUrl()).to.contain('app/management/security/api_keys'); + expect(await pageObjects.apiKeys.getFlyoutTitleText()).to.be('API key details'); + + // Verify name input box are disabled + const apiKeyNameInput = await pageObjects.apiKeys.getApiKeyName(); + expect(await apiKeyNameInput.isEnabled()).to.be(false); + + // Status should be displayed + const apiKeyStatus = await pageObjects.apiKeys.getFlyoutApiKeyStatus(); + expect(await apiKeyStatus).to.be('Expired'); + + const apiKeyMetadataSwitch = await pageObjects.apiKeys.getMetadataSwitch(); + const apiKeyRestrictPrivilegesSwitch = + await pageObjects.apiKeys.getRestrictPrivilegesSwitch(); + + // Verify metadata and restrict privileges switches are now disabled + expect(await apiKeyMetadataSwitch.isEnabled()).to.be(false); + expect(await apiKeyRestrictPrivilegesSwitch.isEnabled()).to.be(false); + + await pageObjects.apiKeys.clickCancelButtonOnApiKeyFlyout(); + }); + + it('should show the `API key details flyout` if the API key does not belong to the user', async () => { + const apiKeyName = 'other-key'; + + await es.security.grantApiKey({ + api_key: { + name: apiKeyName, + }, + grant_type: 'password', + run_as: otherUser.username, + username: 'elastic', + password: 'changeme', + }); + + await browser.refresh(); + + log.debug('API key created, moving on to view'); + + await pageObjects.apiKeys.clickExistingApiKeyToOpenFlyout(apiKeyName); + expect(await browser.getCurrentUrl()).to.contain('app/management/security/api_keys'); + expect(await pageObjects.apiKeys.getFlyoutTitleText()).to.be('API key details'); + + // Verify name input box are disabled + const apiKeyNameInput = await pageObjects.apiKeys.getApiKeyName(); + expect(await apiKeyNameInput.isEnabled()).to.be(false); + + // Status should be displayed + const apiKeyStatus = await pageObjects.apiKeys.getFlyoutApiKeyStatus(); + expect(await apiKeyStatus).to.be('Active'); + + const apiKeyMetadataSwitch = await pageObjects.apiKeys.getMetadataSwitch(); + const apiKeyRestrictPrivilegesSwitch = + await pageObjects.apiKeys.getRestrictPrivilegesSwitch(); + + // Verify metadata and restrict privileges switches are now disabled + expect(await apiKeyMetadataSwitch.isEnabled()).to.be(false); + expect(await apiKeyRestrictPrivilegesSwitch.isEnabled()).to.be(false); + + await pageObjects.apiKeys.clickCancelButtonOnApiKeyFlyout(); + }); + }); + + describe('deletes API key(s)', function () { + before(async () => { + await security.testUser.setRoles(['kibana_admin', 'test_api_keys']); + await pageObjects.common.navigateToUrl('management', 'security/api_keys', { + shouldUseHashForSubUrl: false, + }); + }); + + beforeEach(async () => { + await pageObjects.apiKeys.clickOnPromptCreateApiKey(); + await pageObjects.apiKeys.setApiKeyName('api key 1'); + await pageObjects.apiKeys.clickSubmitButtonOnApiKeyFlyout(); + await ensureApiKeysExist(['api key 1']); + }); + + it('one by one', async () => { + await pageObjects.apiKeys.deleteAllApiKeyOneByOne(); + expect(await pageObjects.apiKeys.getApiKeysFirstPromptTitle()).to.be( + 'Create your first API key' + ); + }); + + it('by bulk', async () => { + await pageObjects.apiKeys.clickOnTableCreateApiKey(); + await pageObjects.apiKeys.setApiKeyName('api key 2'); + await pageObjects.apiKeys.clickSubmitButtonOnApiKeyFlyout(); + + // Make sure all API keys we want to delete are created and rendered. + await ensureApiKeysExist(['api key 1', 'api key 2']); + + await pageObjects.apiKeys.bulkDeleteApiKeys(); + expect(await pageObjects.apiKeys.getApiKeysFirstPromptTitle()).to.be( + 'Create your first API key' + ); + }); + }); + }); +}; diff --git a/x-pack/test_serverless/functional/test_suites/search/empty_page.ts b/x-pack/test_serverless/functional/test_suites/search/empty_page.ts new file mode 100644 index 0000000000000..9808bb69bbeb6 --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/search/empty_page.ts @@ -0,0 +1,41 @@ +/* + * 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 { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getPageObject, getService }: FtrProviderContext) { + const svlSearchNavigation = getService('svlSearchNavigation'); + const testSubjects = getService('testSubjects'); + const svlCommonNavigation = getPageObject('svlCommonNavigation'); + + describe('empty pages', function () { + before(async () => { + await svlSearchNavigation.navigateToLandingPage(); + }); + + it('should show search specific empty page in discover', async () => { + await svlCommonNavigation.sidenav.clickLink({ deepLinkId: 'discover' }); + await testSubjects.existOrFail('kbnOverviewElasticsearchGettingStarted'); + await testSubjects.click('kbnOverviewElasticsearchGettingStarted'); + await svlCommonNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'Getting started' }); + }); + + it('should show search specific empty page in visualize', async () => { + await svlCommonNavigation.sidenav.clickLink({ deepLinkId: 'visualize' }); + await testSubjects.existOrFail('kbnOverviewElasticsearchGettingStarted'); + await testSubjects.click('kbnOverviewElasticsearchGettingStarted'); + await svlCommonNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'Getting started' }); + }); + + it('should show search specific empty page in dashboards', async () => { + await svlCommonNavigation.sidenav.clickLink({ deepLinkId: 'dashboards' }); + await testSubjects.existOrFail('kbnOverviewElasticsearchGettingStarted'); + await testSubjects.click('kbnOverviewElasticsearchGettingStarted'); + await svlCommonNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'Getting started' }); + }); + }); +} diff --git a/x-pack/test_serverless/functional/test_suites/search/index.ts b/x-pack/test_serverless/functional/test_suites/search/index.ts index 9a3f5de27f16c..e4e3021ef8143 100644 --- a/x-pack/test_serverless/functional/test_suites/search/index.ts +++ b/x-pack/test_serverless/functional/test_suites/search/index.ts @@ -10,6 +10,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('serverless search UI', function () { loadTestFile(require.resolve('./landing_page')); + loadTestFile(require.resolve('./empty_page')); loadTestFile(require.resolve('./navigation')); loadTestFile(require.resolve('./cases/attachment_framework')); }); diff --git a/x-pack/test_serverless/functional/test_suites/security/ftr/cases/attachment_framework.ts b/x-pack/test_serverless/functional/test_suites/security/ftr/cases/attachment_framework.ts index a35787cff6aad..6b76503531d3c 100644 --- a/x-pack/test_serverless/functional/test_suites/security/ftr/cases/attachment_framework.ts +++ b/x-pack/test_serverless/functional/test_suites/security/ftr/cases/attachment_framework.ts @@ -30,7 +30,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { await testSubjects.click('solutionSideNavItemLink-dashboards'); - await dashboard.clickNewDashboard(); + await testSubjects.click('createDashboardButton'); await lens.createAndAddLensFromDashboard({}); diff --git a/x-pack/test_serverless/tsconfig.json b/x-pack/test_serverless/tsconfig.json index b986a6134525b..cf52b712de521 100644 --- a/x-pack/test_serverless/tsconfig.json +++ b/x-pack/test_serverless/tsconfig.json @@ -48,5 +48,8 @@ "@kbn/data-views-plugin", "@kbn/core-saved-objects-server", "@kbn/security-api-integration-helpers", + "@kbn/data-view-field-editor-plugin", + "@kbn/data-plugin", + "@kbn/bfetch-plugin", ] } diff --git a/yarn.lock b/yarn.lock index 774ec13a90362..7264182e33b4a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -35,6 +35,20 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" +"@anthropic-ai/sdk@^0.5.7": + version "0.5.10" + resolved "https://registry.yarnpkg.com/@anthropic-ai/sdk/-/sdk-0.5.10.tgz#8cd0b68ac32c71e579b466a89ea30338f2165a32" + integrity sha512-P8xrIuTUO/6wDzcjQRUROXp4WSqtngbXaE4GpEu0PhEmnq/1Q8vbF1s0o7W07EV3j8zzRoyJxAKovUJtNXH7ew== + dependencies: + "@types/node" "^18.11.18" + "@types/node-fetch" "^2.6.4" + abort-controller "^3.0.0" + agentkeepalive "^4.2.1" + digest-fetch "^1.3.0" + form-data-encoder "1.7.2" + formdata-node "^4.3.2" + node-fetch "^2.6.7" + "@apidevtools/json-schema-ref-parser@^9.0.6": version "9.0.9" resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz#d720f9256e3609621280584f2b47ae165359268b" @@ -4250,6 +4264,10 @@ version "0.0.0" uid "" +"@kbn/elastic-assistant-plugin@link:x-pack/plugins/elastic_assistant": + version "0.0.0" + uid "" + "@kbn/elastic-assistant@link:x-pack/packages/kbn-elastic-assistant": version "0.0.0" uid "" @@ -4962,6 +4980,10 @@ version "0.0.0" uid "" +"@kbn/no-data-page-plugin@link:src/plugins/no_data_page": + version "0.0.0" + uid "" + "@kbn/notifications-plugin@link:x-pack/plugins/notifications": version "0.0.0" uid "" @@ -9241,7 +9263,7 @@ dependencies: "@types/node" "*" -"@types/node-fetch@2.6.4": +"@types/node-fetch@2.6.4", "@types/node-fetch@^2.6.4": version "2.6.4" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.4.tgz#1bc3a26de814f6bf466b25aeb1473fa1afe6a660" integrity sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg== @@ -9271,7 +9293,7 @@ dependencies: "@types/node" "*" -"@types/node@*", "@types/node@18.17.1", "@types/node@>= 8", "@types/node@>=12.12.47", "@types/node@>=13.7.0", "@types/node@>=8.9.0", "@types/node@^10.1.0", "@types/node@^14.0.10 || ^16.0.0", "@types/node@^14.14.20 || ^16.0.0", "@types/node@^14.14.31": +"@types/node@*", "@types/node@18.17.1", "@types/node@>= 8", "@types/node@>=12.12.47", "@types/node@>=13.7.0", "@types/node@>=8.9.0", "@types/node@^10.1.0", "@types/node@^14.0.10 || ^16.0.0", "@types/node@^14.14.20 || ^16.0.0", "@types/node@^14.14.31", "@types/node@^18.11.18": version "18.17.1" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.17.1.tgz#84c32903bf3a09f7878c391d31ff08f6fe7d8335" integrity sha512-xlR1jahfizdplZYRU59JlUx9uzF1ARa8jbhM11ccpCJya8kvos5jwdm2ZAgxSCwOl0fq21svP18EVwPBXMQudw== @@ -9605,7 +9627,7 @@ dependencies: "@types/node" "*" -"@types/retry@^0.12.0": +"@types/retry@0.12.0", "@types/retry@^0.12.0": version "0.12.0" resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== @@ -9824,6 +9846,11 @@ resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.0.tgz#53ef263e5239728b56096b0a869595135b7952d2" integrity sha512-kr90f+ERiQtKWMz5rP32ltJ/BtULDI5RVO0uavn1HQUOwjx0R1h0rnDYNL0CepF1zL5bSY6FISAfd9tOdDhU5Q== +"@types/uuid@^9.0.1": + version "9.0.2" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.2.tgz#ede1d1b1e451548d44919dc226253e32a6952c4b" + integrity sha512-kNnC1GFBLuhImSnV7w4njQkUiJi0ZXUycu1rUaouPqiKlXkh77JKgdRnTAp1x5eBwcIwbtI+3otwzuIDEuDoxQ== + "@types/vfile-message@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@types/vfile-message/-/vfile-message-2.0.0.tgz#690e46af0fdfc1f9faae00cd049cc888957927d5" @@ -10405,6 +10432,13 @@ abbrev@1, abbrev@^1.0.0: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" @@ -11565,12 +11599,17 @@ balanced-match@^2.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-2.0.0.tgz#dc70f920d78db8b858535795867bf48f820633d9" integrity sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA== +base-64@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb" + integrity sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA== + base64-js@1.3.1, base64-js@^1.0.2, base64-js@^1.2.0, base64-js@^1.3.0, base64-js@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== -base64-js@^1.1.2: +base64-js@^1.1.2, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -11663,7 +11702,12 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c" integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow== -binary-search@^1.3.3: +binary-extensions@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +binary-search@^1.3.3, binary-search@^1.3.5: version "1.3.6" resolved "https://registry.yarnpkg.com/binary-search/-/binary-search-1.3.6.tgz#e32426016a0c5092f0f3598836a1c7da3560565c" integrity sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA== @@ -12287,6 +12331,11 @@ camelcase@5.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42" integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA== +camelcase@6: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + camelcase@^2.0.0, camelcase@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" @@ -12475,7 +12524,7 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -charenc@~0.0.1: +charenc@0.0.2, charenc@~0.0.1: version "0.0.2" resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= @@ -13401,7 +13450,7 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -crypt@~0.0.1: +crypt@0.0.2, crypt@~0.0.1: version "0.0.2" resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= @@ -14644,6 +14693,14 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" +digest-fetch@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/digest-fetch/-/digest-fetch-1.3.0.tgz#898e69264d00012a23cf26e8a3e40320143fc661" + integrity sha512-CGJuv6iKNM7QyZlM2T3sPAdZWd/p9zQiRNS9G+9COUCwzWFTs0Xp8NF5iePx7wtvhDykReiRRrSeNb4oMmB8lA== + dependencies: + base-64 "^0.1.0" + md5 "^2.3.0" + dir-glob@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" @@ -15941,6 +15998,11 @@ event-emitter@^0.3.5, event-emitter@~0.3.5: d "1" es5-ext "~0.10.14" +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + eventemitter-asyncresource@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz#734ff2e44bf448e627f7748f905d6bdd57bdb65b" @@ -15951,7 +16013,7 @@ eventemitter2@6.4.7: resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.7.tgz#a7f6c4d7abf28a14c1ef3442f21cb306a054271d" integrity sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg== -eventemitter3@^4.0.0: +eventemitter3@^4.0.0, eventemitter3@^4.0.4: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== @@ -16119,6 +16181,11 @@ expose-loader@^0.7.5: resolved "https://registry.yarnpkg.com/expose-loader/-/expose-loader-0.7.5.tgz#e29ea2d9aeeed3254a3faa1b35f502db9f9c3f6f" integrity sha512-iPowgKUZkTPX5PznYsmifVj9Bob0w2wTHVkt/eYNPSzyebkUgIedmskf/kcfEIWpiWjg3JRjnW+a17XypySMuw== +expr-eval@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/expr-eval/-/expr-eval-2.0.2.tgz#fa6f044a7b0c93fde830954eb9c5b0f7fbc7e201" + integrity sha512-4EMSHGOPSwAfBiibw3ndnP0AvjDWLsMvGOvWEZ2F96IGk0bIVdjQisOHxReSkE13mHcfbuCiXw+G4y0zv6N8Eg== + express@^4.17.1, express@^4.17.3: version "4.17.3" resolved "https://registry.yarnpkg.com/express/-/express-4.17.3.tgz#f6c7302194a4fb54271b73a1fe7a06478c8f85a1" @@ -16776,6 +16843,11 @@ fork-ts-checker-webpack-plugin@^6.0.4: semver "^7.3.2" tapable "^1.0.0" +form-data-encoder@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.2.tgz#1f1ae3dccf58ed4690b86d87e4f57c654fbab040" + integrity sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A== + form-data@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" @@ -16808,6 +16880,14 @@ format@^0.2.0: resolved "https://registry.yarnpkg.com/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b" integrity sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs= +formdata-node@^4.3.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/formdata-node/-/formdata-node-4.4.1.tgz#23f6a5cb9cb55315912cbec4ff7b0f59bbd191e2" + integrity sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ== + dependencies: + node-domexception "1.0.0" + web-streams-polyfill "4.0.0-beta.3" + formdata-polyfill@^4.0.10: version "4.0.10" resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423" @@ -18643,6 +18723,11 @@ is-alphanumerical@^1.0.0: is-alphabetical "^1.0.0" is-decimal "^1.0.0" +is-any-array@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-any-array/-/is-any-array-2.0.1.tgz#9233242a9c098220290aa2ec28f82ca7fa79899e" + integrity sha512-UtilS7hLRu++wb/WBAw9bNuP1Eg04Ivn1vERJck8zJthEvXCBEBpGR/33u/xLKWEQf95803oalHrVDptcAvFdQ== + is-arguments@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" @@ -18686,7 +18771,7 @@ is-boolean-object@^1.0.1, is-boolean-object@^1.1.0: dependencies: call-bind "^1.0.0" -is-buffer@^1.1.5, is-buffer@~1.1.1: +is-buffer@^1.1.5, is-buffer@~1.1.1, is-buffer@~1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== @@ -20087,6 +20172,13 @@ js-string-escape@^1.0.1: resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef" integrity sha1-4mJbrbwNZ8dTPp7cEGjFh65BN+8= +js-tiktoken@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/js-tiktoken/-/js-tiktoken-1.0.7.tgz#56933fcd2093e8304060dfde3071bda91812e6f5" + integrity sha512-biba8u/clw7iesNEWLOLwrNGoBP2lA+hTaBLs/D45pJdUPFXyxD6nhcDVtADChghv4GgyAiMKYMiRx7x6h7Biw== + dependencies: + base64-js "^1.5.1" + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -20283,6 +20375,11 @@ jsonparse@^1.2.0: resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= +jsonpointer@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-5.0.1.tgz#2110e0af0900fd37467b5907ecd13a7884a1b559" + integrity sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ== + jsonwebtoken@^9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz#d0faf9ba1cc3a56255fe49c0961a67e520c1926d" @@ -20452,6 +20549,44 @@ kuler@^2.0.0: resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== +langchain@^0.0.132: + version "0.0.132" + resolved "https://registry.yarnpkg.com/langchain/-/langchain-0.0.132.tgz#2cdcc5d7078c70aa403f7eaeff3556c50a485632" + integrity sha512-gXnuiAhsQQqXheKQiaSmFa9s3S/Yhkkb9OCytu04OE0ecttvVvfjjqIoNVS9vor8V7kRUgYPKHJsMz2UFDoJNw== + dependencies: + "@anthropic-ai/sdk" "^0.5.7" + ansi-styles "^5.0.0" + binary-extensions "^2.2.0" + camelcase "6" + decamelize "^1.2.0" + expr-eval "^2.0.2" + flat "^5.0.2" + js-tiktoken "^1.0.7" + js-yaml "^4.1.0" + jsonpointer "^5.0.1" + langsmith "~0.0.16" + ml-distance "^4.0.0" + object-hash "^3.0.0" + openai "^3.3.0" + openapi-types "^12.1.3" + p-queue "^6.6.2" + p-retry "4" + uuid "^9.0.0" + yaml "^2.2.1" + zod "^3.21.4" + zod-to-json-schema "^3.20.4" + +langsmith@~0.0.16: + version "0.0.26" + resolved "https://registry.yarnpkg.com/langsmith/-/langsmith-0.0.26.tgz#a63f911a3113860de5488392a46468d1b482e3ef" + integrity sha512-TecBjdgYGMxNaWp2L2X0OVgu8lge2WeQ5UpDXluwF3x+kH/WHFVSuR1RCuP+k2628GSVFvXxVIyXvzrHYxrZSw== + dependencies: + "@types/uuid" "^9.0.1" + commander "^10.0.1" + p-queue "^6.6.2" + p-retry "4" + uuid "^9.0.0" + language-subtag-registry@~0.3.2: version "0.3.21" resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz#04ac218bea46f04cb039084602c6da9e788dd45a" @@ -21337,6 +21472,15 @@ md5@^2.1.0: crypt "~0.0.1" is-buffer "~1.1.1" +md5@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" + integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g== + dependencies: + charenc "0.0.2" + crypt "0.0.2" + is-buffer "~1.1.6" + mdast-squeeze-paragraphs@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-4.0.0.tgz#7c4c114679c3bee27ef10b58e2e015be79f1ef97" @@ -21987,6 +22131,42 @@ mkdirp@^1.0.3, mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== +ml-array-mean@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/ml-array-mean/-/ml-array-mean-1.1.6.tgz#d951a700dc8e3a17b3e0a583c2c64abd0c619c56" + integrity sha512-MIdf7Zc8HznwIisyiJGRH9tRigg3Yf4FldW8DxKxpCCv/g5CafTw0RRu51nojVEOXuCQC7DRVVu5c7XXO/5joQ== + dependencies: + ml-array-sum "^1.1.6" + +ml-array-sum@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/ml-array-sum/-/ml-array-sum-1.1.6.tgz#d1d89c20793cd29c37b09d40e85681aa4515a955" + integrity sha512-29mAh2GwH7ZmiRnup4UyibQZB9+ZLyMShvt4cH4eTK+cL2oEMIZFnSyB3SS8MlsTh6q/w/yh48KmqLxmovN4Dw== + dependencies: + is-any-array "^2.0.0" + +ml-distance-euclidean@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ml-distance-euclidean/-/ml-distance-euclidean-2.0.0.tgz#3a668d236649d1b8fec96380b9435c6f42c9a817" + integrity sha512-yC9/2o8QF0A3m/0IXqCTXCzz2pNEzvmcE/9HFKOZGnTjatvBbsn4lWYJkxENkA4Ug2fnYl7PXQxnPi21sgMy/Q== + +ml-distance@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/ml-distance/-/ml-distance-4.0.1.tgz#4741d17a1735888c5388823762271dfe604bd019" + integrity sha512-feZ5ziXs01zhyFUUUeZV5hwc0f5JW0Sh0ckU1koZe/wdVkJdGxcP06KNQuF0WBTj8FttQUzcvQcpcrOp/XrlEw== + dependencies: + ml-array-mean "^1.1.6" + ml-distance-euclidean "^2.0.0" + ml-tree-similarity "^1.0.0" + +ml-tree-similarity@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/ml-tree-similarity/-/ml-tree-similarity-1.0.0.tgz#24705a107e32829e24d945e87219e892159c53f0" + integrity sha512-XJUyYqjSuUQkNQHMscr6tcjldsOoAekxADTplt40QKfwW6nd++1wHWV9AArl0Zvw/TIHgNaZZNvr8QGvE8wLRg== + dependencies: + binary-search "^1.3.5" + num-sort "^2.0.0" + mocha-junit-reporter@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-2.0.2.tgz#d521689b651dc52f52044739f8ffb368be415731" @@ -22454,7 +22634,7 @@ node-dir@^0.1.10: dependencies: minimatch "^3.0.2" -node-domexception@^1.0.0: +node-domexception@1.0.0, node-domexception@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== @@ -22760,6 +22940,11 @@ null-loader@^3.0.0: loader-utils "^1.2.3" schema-utils "^1.0.0" +num-sort@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/num-sort/-/num-sort-2.1.0.tgz#1cbb37aed071329fdf41151258bc011898577a9b" + integrity sha512-1MQz1Ed8z2yckoBeSfkQHHO9K1yDRxxtotKSJ9yvcTUUxSvfvzEq5GwBrjjHEpMlq/k5gvXdmJ1SbYxWtpNoVg== + num2fraction@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" @@ -22837,6 +23022,11 @@ object-hash@^1.3.0, object-hash@^1.3.1: resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df" integrity sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA== +object-hash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" + integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== + object-identity-map@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/object-identity-map/-/object-identity-map-1.0.2.tgz#2b4213a4285ca3a8cd2e696782c9964f887524e7" @@ -23043,6 +23233,11 @@ openapi-types@^10.0.0: resolved "https://registry.yarnpkg.com/openapi-types/-/openapi-types-10.0.0.tgz#0debbf663b2feed0322030b5b7c9080804076934" integrity sha512-Y8xOCT2eiKGYDzMW9R4x5cmfc3vGaaI4EL2pwhDmodWw1HlK18YcZ4uJxc7Rdp7/gGzAygzH9SXr6GKYIXbRcQ== +openapi-types@^12.1.3: + version "12.1.3" + resolved "https://registry.yarnpkg.com/openapi-types/-/openapi-types-12.1.3.tgz#471995eb26c4b97b7bd356aacf7b91b73e777dd3" + integrity sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw== + opener@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" @@ -23270,11 +23465,27 @@ p-map@^4.0.0: dependencies: aggregate-error "^3.0.0" +p-queue@^6.6.2: + version "6.6.2" + resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426" + integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ== + dependencies: + eventemitter3 "^4.0.4" + p-timeout "^3.2.0" + p-reflect@2.1.0, p-reflect@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-reflect/-/p-reflect-2.1.0.tgz#5d67c7b3c577c4e780b9451fc9129675bd99fe67" integrity sha512-paHV8NUz8zDHu5lhr/ngGWQiW067DK/+IbJ+RfZ4k+s8y4EKyYCz8pGYWjxCg35eHztpJAt+NUgvN4L+GCbPlg== +p-retry@4: + version "4.6.2" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16" + integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ== + dependencies: + "@types/retry" "0.12.0" + retry "^0.13.1" + p-retry@^4.2.0, p-retry@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.5.0.tgz#6685336b3672f9ee8174d3769a660cb5e488521d" @@ -23298,6 +23509,13 @@ p-timeout@^2.0.1: dependencies: p-finally "^1.0.0" +p-timeout@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" + integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== + dependencies: + p-finally "^1.0.0" + p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" @@ -26197,6 +26415,11 @@ retry@^0.12.0: resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= +retry@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" + integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== + reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" @@ -30058,11 +30281,21 @@ web-namespaces@^1.0.0: resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec" integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw== +web-streams-polyfill@4.0.0-beta.3: + version "4.0.0-beta.3" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz#2898486b74f5156095e473efe989dcf185047a38" + integrity sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug== + web-streams-polyfill@^3.0.3, web-streams-polyfill@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz#a6b74026b38e4885869fb5c589e90b95ccfc7965" integrity sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA== +web-streams-polyfill@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6" + integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q== + web-streams-polyfill@~3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.0.3.tgz#f49e487eedeca47a207c1aee41ee5578f884b42f" @@ -30722,7 +30955,7 @@ yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yaml@^2.2.2: +yaml@^2.2.1, yaml@^2.2.2: version "2.3.1" resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b" integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ== @@ -30882,6 +31115,11 @@ zip-stream@^4.1.0: compress-commons "^4.1.0" readable-stream "^3.6.0" +zod-to-json-schema@^3.20.4: + version "3.21.4" + resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.21.4.tgz#de97c5b6d4a25e9d444618486cb55c0c7fb949fd" + integrity sha512-fjUZh4nQ1s6HMccgIeE0VP4QG/YRGPmyjO9sAh890aQKPEk3nqbfUXhMFaC+Dr5KvYBm8BCyvfpZf2jY9aGSsw== + zod@^3.21.4: version "3.21.4" resolved "https://registry.yarnpkg.com/zod/-/zod-3.21.4.tgz#10882231d992519f0a10b5dd58a38c9dabbb64db"
    + + + {children} + + +
    +
    + + {children} + +
    +